0

I am currently working on a project for class. Unfortunately I had some personal issues at home this semester, so I have been trying to catch up including this class, so sorry if the question may sound dumb or code looks horrendous.

I have to make a game. I drew the border, game grid etc and I basically wanted to print out digits including values over 10 to the screen. I have it down how to convert to ascii codes.

I was going to have an array of values for the game. I wanted to test with 1 value, 0, first to print across the screen before I put a bunch of numbers. I am doing this on emu8086 btw I have five variables under my data segment, the two I'm referring to is

numbers dw 0 ; this was gonna have multiple values (an array), started off with 1 value
digitOne dw 0 ; this was suppose to represent a digit, I can make it also ? instead

Take note they are both dw's and have the same value, 0

Now, I have a working for loop to print values in the right places on the screen. I'm in textmode, at 80x25 (ax 0003h to be exact)

This code below is in the loop. I have another variable in the data segment called counter which counts for how many boxes I'm filling on the grid, and then shifts to the second row on the grid once the counter reaches a certain amount and so on until i fill the final box.

mov dx, [numbers]      ; assign variable value
    add dx, 48              ; gives the ascii code 48 to the lower bit
    mov dh, 2fh               ; gives attribute color
    mov ptr es: [bx], dx      ; displays on screen

When I do this, dx receives an unsigned value of 205 when assigning numbers value to it (this is before add 48)

But when i use

mov dx, [digitOne]      ; assign variable value
    add dx, 48              ; gives the ascii code 48 to the lower bit
    mov dh, 2fh               ; gives attribute color
    mov ptr es: [bx], dx

It works fine. dx gets a value of 0 , then adding 48 gives the ascii code 48, which prints 0's. The previous one is getting an ascii value -3 which is printing tiny 2's.

Any ideas what is happening?

Also, my other two issues are my other variable, digitTwo. When I switch it from db to dw, my game no longer runs in 80x25, it goes to 40x25. I have no idea why. Making it db again brings it back to 80x25. Same thing happens if I just try to declare a new variable.

Another issue is, when I compile this to a com file to run on dosbox with TASM, let's say the 0's print successfully, they're all indented a certain amount over to the right. Why?

I don't know if this is right, but I feel like maybe I'm running out of memory or I'm not cleaning up correctly?

The rest of my code are basically just for loops creating a border around the screen and then a grid for the game. Those draw fine.

I would try to do this using TASM and dosbox, but it seems dosbox crashes for most programs, even example programs I found online. I read it may have to do with I'm on a 64 bit machine. That's why I downloaded emu8086

Thank you for reading

  • 1
    We would need a [mcve] for this. First guess would be that you’re overwriting the values in memory in some other parts of the code. – Sami Kuhmonen Nov 12 '17 at 06:07
  • One moment, I will try to gather that then :) – TheCoolest2 Nov 12 '17 at 06:09
  • 1
    is your `ds` segment register initialized correctly? That's the usual explanation for loads getting unexpected data in 16-bit code. (single-step with a debugger to see for sure what value is getting loaded.) – Peter Cordes Nov 12 '17 at 06:12
  • Hello. I updated above. Peter, do you mean ax @data and then ds, ax? – TheCoolest2 Nov 12 '17 at 06:19
  • I upvoted Peter: It looks like a linker issue. Not to familiar with TASM, The dw constants are not linked in properly. So they get a linker defined value that does not point anywhere. Try defining a big db array and check that the values match when reading. – starmole Nov 12 '17 at 06:23
  • I am not in TASM in this code. I'm using emu8086. The TASM is another seperate issue :). Peter, when I go to the emulator, DS is initialized with a lower bit of 0 and a higher bit of 7 – TheCoolest2 Nov 12 '17 at 06:27
  • So i googled around a bit: Make sure your dw variables are under .DATA, and your code is under .CODE. If variables are declared under .CODE you need to prefix with cs, the default is ds, which is .DATA – starmole Nov 12 '17 at 06:31
  • This might be helpful: https://stackoverflow.com/questions/27201947/tasm-local-variables – starmole Nov 12 '17 at 06:32
  • Hello. I'm afraid that did not seem to solve either issue. – TheCoolest2 Nov 12 '17 at 06:45
  • 1
    Terminology: I think you mean higher "half" or "byte" 7, not higher "bit". It's a 16-bit register, and a bit can only be 0 or 1 so 7 doesn't make any sense. Anyway, there's no reason to talk about the two halves of `ds` separately. You have `ds` = `0x0700`. Anyway, yes, `mov ax, @data` / `mov ds,ax` is exactly what you should be doing. And now I see that you *are* doing that, since you fixed the code formatting. I was only skimming because this question is long and x86-16 is obsolete and uninteresting to me (sorry). But it's not a wall of code or text, so someone should answer. – Peter Cordes Nov 12 '17 at 06:59
  • Sorry, I meant byte. I too often mix the words without thinking. I did have the mov ax earlier, and just started removing random things if something would trip up. I forgot to put it back. Interestingly enough, when I use xchg dx, [numbers] instead of mov, The first type dx is incorrect, but all other times, dx has the correct values I hope someone can answer :( I have been struggling all day and this thing counts for 50% of my grade and I'm stuck on this. – TheCoolest2 Nov 12 '17 at 07:03
  • dosbox works perfectly fine for me (64b machine too, linux "neon" distribution, it's based on Ubuntu 16.04), you mean the dosbox itself does crash for you? Did you check if you have some recent version, or the dosbox forum? I find it hard to believe such popular SW has some common crash. – Ped7g Nov 12 '17 at 07:13
  • Anyway, your posted code is not [MCVE] ...to obfuscate ASCII codes, you can use some neutral value which is good enough for debugging, like the [3-lines symbol `240`](http://www.asciitable.com/) ... but `mov ptr ` would not work? was there `mov word ptr` or `mov byte ptr`? etc. Anyway I have strong suspicion the code you posted would work, and not demonstrate the problem you have, as you are very likely overwriting some memory elsewhere in your code. – Ped7g Nov 12 '17 at 07:18
  • Here, I put it in a paste bin https://pastebin.com/6EQnyx7z. I put 240 like you said and it returns a totally different ascii, I believ 187 – TheCoolest2 Nov 12 '17 at 07:28
  • Yes I have the latest version. Any code I try to run it either freezes and closes, or just doesn't do anything. When I run a code in something like emu8086, it works fine. I'm on Win 10 64bit. After some research, I saw many people were having issues on win 10 64bit, so I downloaded emu8086 No, I'm saying mov dx, [variablename] is the issue. Not mov ptr – TheCoolest2 Nov 12 '17 at 07:30
  • `mov dx,[label]` can't be issue, the CPU will simply do that, load 2 bytes from address `label`. Your issue is, that you expect certain value stored in that memory, and you receive different one. And now you have to hunt down, where the unwanted value comes from. Use emu8086 debugger and view memory at address `label`, check after loading `ds`, that `mov dx,[..]` loads your value defined (`dw 0` => `0` value), and in memory view you should see two zero bytes. Then put some breakpoints here and there and try to find in which part of code the memory does change to 205. – Ped7g Nov 12 '17 at 07:49
  • And under TASM your code will not work as .COM file, because COM files are both code+data+stack in single segment, loaded at offset `0x100` by DOS (reason of `org 100h` usage), and the DOS jumps there. If you forced TASM to build it as COM file, then the data will get executed first, instead of code. In COM you have to start with instructions, and add data between/after routines, or even use more segments from upper memory, if you don't fit into 64k. EXE file would be ok, the linker will put different data/code sections into it. – Ped7g Nov 12 '17 at 07:53
  • tried to compile with TASM, but it's such a mess (syntactically)... makes me wonder, if emu8086 would compile even random text... ... can you really compile that pastebin as is in your emu8086? If yes, send them some award... eventually try some real assembler, which for example reports `jmp` without any target as error. But the code looks incomplete, even doesn't terminate in any way, so it will execute some random memory after initial things... – Ped7g Nov 12 '17 at 08:12
  • Hello. I followed it at the memory, and it seemed to just stay at 0 throughout the whole program. But, I did what you said by moving the data into the single segment and wow it worked. I cannot believe what an easy fix it was. It fixed the issues. Sorry I'm still a bit confused, how does the data being executed first affect the outcome in the end? – TheCoolest2 Nov 12 '17 at 08:24
  • Yes, I can compile it, emu8086 shows documentation where it converts to TASM syntax, so I was very confused why it wouldn't work. Yes I know, it looks very incomplete, it's considered to be beginner friendly, it confuses me because I wanted to use TASM as I'm use to that format and syntax but it seems dosbox has weird problems for me. I was going to try my desktop, but power loss last week in my city caused a voltage dip and damaged my desktop. – TheCoolest2 Nov 12 '17 at 08:27
  • After fixing all the syntax problems, using TASM+TD, the code works as expected under TASM. The `mov dx, [numbers]` loads zero into `dx`. ... BTW, that `240` I meant as fake ascii code for the grid chars, so you don't show the real ones to your class cheaters, but to provide stackoverflow with WORKING example. I can write answer with summary about syntax problems, if you are interested. Executing data as code is huge problem, as the CPU doesn't know it's data, for CPU it's zero byte like zero byte, i.e. it will execute the instruction which has opcode zero, thus its running some garbage code. – Ped7g Nov 12 '17 at 08:28
  • Ah I got it. Thanks! And sure go ahead! I'll choose it as answer. I temporarily removed some code so no cheaters. But I will return it after due date this week. :) Thank you all for help! – TheCoolest2 Nov 12 '17 at 08:32

1 Answers1

0

First the main reason for most of the "weird" behaviour happening.

You were assembling+running it as COM file, which is one of the two DOS executable types.

The COM file is raw binary (65280 bytes long at most (65536-256)), which is loaded into some segment of free memory from offset 100h (the first 256 bytes contain things like command line (arguments), and other DOS environment values, can't recall details, IIRC that zone is called PSP). After the DOS will load the binary, it will jump to that offset 100h.

Your code did start with data, so they were executed by CPU as instructions, causing some other damage/discrepancies (like the 40x25 text mode, etc).

When EXE type of binary would be used, it would work fine, as the EXE contains further meta data about entry point (end start in your full source specifies the entry point for EXE, but not for COM) and can load data into separate data segment. And also can contain several code/data segments, so the total size of EXE can be much larger (if you have big code base which doesn't fit into single segment).


Now about syntax problems, to make your source compilable with TASM, and some hints about code itself.


segment .data - not recognized by TASM, you have to use either just .data - shortcut to define data segment, or full-fledged segment definition, including ends, etc. (check TASM documentation).

segment .code - same story, just .code is correct in TASM

But neither .data or .code is needed if you are targetting COM binary, then only the org 100h is important (and start with code immediately, even if it's just jmp start followed by data). And vice versa, with EXE target the org 100h shouldn't be there, let the linker specify memory map of EXE executable, it has some reasonable defaults. So pick one type, and stick to it. In TASM you can pre-select assembler behaviour by using .model small for EXE, or .model tiny for COM (there are also bigger models, but you will not need those). In emu8086 there's some help/docs file, where the directives for COM/EXE are explained, I don't remember and don't even want to know, as emu8086 is IMO quite atrocious (but hey, as long as it works for you...).

BTW, those .data/.code fixes will very likely work in emu8086 too, as it basically ignores almost everything it does not fully understand, not enforcing any particular syntax.


mov ax, 0003h ; Size of 80x25 (al=00, ah=03)

Wrong comment, AL is lower byte, AH is higher byte, so 0003h is AH=0, AL=3.


Clearing screen: wow, that's quite some abuse of "Scroll active page up" service, impressive for figuring it out. But there's simpler way to clear screen just by overwriting video memory:

start:
    mov ax, @data               ; ds = data segment
    mov ds, ax
    mov ax, 0B800h              ; es = text mode video memory segment
    mov es, ax

    mov ax, 0003h ; Size of 80x25 (al=00, ah=03)
    int 10h

    ; clear screen
    xor di, di                  ; di = video memory offset 0
    mov ax, 02F00h + ' '        ; write space with green background and white text
    mov cx, 80*25               ; 80*25 character+attribute pairs
    rep stosw                   ; overwrite video memory (clears screen)

About the manual drawing of board... let's start with syntax problems.

mov ptr es: [bx], dx

The standalone ptr directive means nothing in this context (not even sure if there is any context when it would in MASM/TASM mean something). If you want say that memory should be written, the square brackets are enough, i.e mov es:[bx],dx. If you want to specify the size of data to write, then you have to specify it mov byte ptr es:[bx],dx, asking assembler to produce instruction which will store byte at address es:bx from dx - which will be reported as error in TASM, because dx is of word size, and such instruction doesn't exist. (working alternatives would be mov word ptr es:[bx],dx or mov byte ptr es:[bx],dl).

Usually when the value to be stored is taken from register, the size specifier is not written in source, as it's sort of "obvious" from the size of register used. I.e. dx to write = word to write.

But then you draw the grid with instructions like:

mov ptr es: [bx], 240

Where the assembler can't deduct the size of 240, so the size modifier is needed and strongly recommended (some assemblers will silently guess size by the value, but such source is hard to read+debug, because you don't know which value did want programmer to store). So fix that with: mov byte ptr es: [bx], 240 (as you want to write only the ASCII part, not attribute -> byte size only).

And finally the code style, I will dissect the first loop only:

mov bx, 2                   ; start at offset 2 (top horizontal line of border)

I'm almost happy with this one, although the comment is not completely "intent-like". I would rather describe it as ; offset of [1, 0] character ([x,y]) ... and I would probably do mov bx, ( 0*80 + 1)*2 so I can use identical lines in code later (I even put there extra space ahead 0 and 1 to allow for two-digit coordinates later), but changing only coordinates, see below.

mov di, 3842                ; start offset 3842 (bottom horizontal line of border)

Now here it becomes plain fugly... the "bottom horizontal" is somewhat saving it, but still very cryptic, consider this alternative:

mov di, (24*80 +  1)*2      ; offset of [1, 24] character ([x,y])

This way you make the assembler to do the calculation stuff for you during assembling. The resulting machine code is of course identical, value 3842 in both cases, but in this way you can read the source and understand what that offset should represent.

l1:
   mov byte ptr es: [bx], 240    ; display fake at "coordinate" bx
   mov byte ptr es: [di], 240    ; same as above
   add bx, 2                ; move 2 horizontally
   add di, 2                ; same as above

This is quite fine. It would be possible to use only single offset register bx and the second write to be +24 lines away like:

   mov byte ptr es: [bx + (24*80*2)], 196    ; bottom line

Which would allow you to delete all that di stuff as redundant, but I would in the end do the whole thing differently, as I will post below, this is just an idea how often in assembly you can use fewer registers, when something is just offset-away from something you already have.

   cmp bx, 158              ; compare to the width of the window (160-2 because want room for a box corner)
   jl l1                    ; loop if less than the width

Again "what is 158" AND jl. jl is short for "jump less", and that's signed-arithmetic. But offsets are unsigned. In text mode you will not hit a problem with it, because even bottom of screen is only about 4k offset, but in pixel 320x200 mode the bottom pixels are at offsets around 64000, and that's already negative value when you treat it as 16b signed, so the jl would not loop in such case. jb is the more correct one, for comparing memory offsets.

   cmp bx, ( 0*80 +  79)*2  ; write up to [79, 0] character (last line at [78,0], leaving room for corner)
   jb l1                    ; loop if below that

Then later when you fix corners, you have code like this:

mov bx, 3998
mov byte ptr es: [bx], 188

That's a bit overcomplicating things, x86 does have also MOV r/m8,imm8 variant, so you can do directly:

mov byte ptr es:[3998], 188

Check this answer for all legal variants of addressing operand in 16b real mode of x86 x86 16-bit addressing modes. (In 32/64 bit protected mode there are lot more variants available, if you see things like mov al,[esi+edx*4], that's legal x86 instruction in 32b mode, but not in 16b)

And finally that's lot of code which repeats a lot, how about using some subroutines to make it shorter? My try (whoa, still about ~100 lines, but compare with yours):

    ... after screen is set and cleared...

    ; big box around whole screen
    mov al,196                  ; horizontal line ASCII
    mov cx,78                   ; 78 characters to draw (80-2, first and last are for corners)
    mov di, ( 0*80 +  1)*2      ; offset of [1, 0] character ([x,y])
    call FillCharOnly_horizontal    ; top line
    mov di, (24*80 +  1)*2      ; offset of [1, 0] character ([x,y])
    call FillCharOnly_horizontal    ; bottom line
    mov al,179                  ; vertical line ASCII
    mov cx,23                   ; 23 characters to draw (25-2, first and last are for corners)
    mov di, ( 1*80 +  0)*2      ; offset of [0, 1] character ([x,y])
    call FillCharOnly_vertical  ; left line
    mov di, ( 1*80 + 79)*2      ; offset of [79, 1] character ([x,y])
    call FillCharOnly_vertical  ; right line
    ; corners are done separately
    mov byte ptr es:[( 0*80 +  0)*2],218
    mov byte ptr es:[( 0*80 + 79)*2],191
    mov byte ptr es:[(24*80 +  0)*2],192
    mov byte ptr es:[(24*80 + 79)*2],217

    ; draw 4x4 boxes (13x5 box size) with 5 horizontal and 5 vertical lines
    ; 5 horizontal lines first
    mov ax, 196 + (5*256)       ; horizontal line in AL + counter=5 in AH
    mov cx, 4*13                ; each box is 13 characters wide
    mov di, ( 2*80 +  2)*2      ; offset of [2, 2] character ([x,y])
    push di                     ; will be also starting point for vertical lines
boxes_horizontal_loop:
    push di                     ; store the offset
    call FillCharOnly_horizontal    ; horizontal edge of boxes
    pop di
    add di, ( 5*80 +  0)*2      ; next horizontal is [+0, +5] away
    dec ah
    jnz boxes_horizontal_loop
    ; 5 vertical lines then
    mov ax, 179 + (5*256)       ; vertical line in AL + counter=5 in AH
    mov cx, 4*5                 ; each box is 5 characters tall
    pop di                      ; use same starting point as horizontal lines
boxes_vertical_loop:
    push di                     ; store the offset
    call FillCharOnly_vertical  ; horizontal edge of boxes
    pop di
    add di, ( 0*80 + 13)*2      ; next vertical is [+13, +0] away
    dec ah
    jnz boxes_vertical_loop
    ; fix corners and crossings - first the 4 main corners
    mov byte ptr es:[( 2*80 +  2)*2],218
    mov byte ptr es:[( 2*80 + 54)*2],191
    mov byte ptr es:[(22*80 +  2)*2],192
    mov byte ptr es:[(22*80 + 54)*2],217
    ; now the various crossings of the game box itself
    mov cx,3                    ; 3 of top "T" (count)
    mov dx,13*2                 ; offset delta (+13 characters to right)
    mov al,194                  ; top "T"
    mov di, ( 2*80 + 15)*2      ; offset of [2+13, 2] character ([x,y])
    call FillCharOnly
    mov al,193                  ; bottom "T"
    mov di, (22*80 + 15)*2      ; offset of [2+13, 22] character ([x,y])
    call FillCharOnly           ; CX+DX was preserved by the call, still same
    mov al,195                  ; left "T"
    mov di, ( 7*80 +  2)*2      ; offset of [2, 2+5] character ([x,y])
    mov dx,(5*80)*2             ; offset delta (+5 lines below)
    call FillCharOnly           ; CX is still 3, DX was updated
    mov al,180                  ; right "T"
    mov di, ( 7*80 + 54)*2      ; offset of [2+52, 2+5] character ([x,y])
    call FillCharOnly           ; CX+DX was preserved by the call, still same
    mov al,197                  ; crossings inside "+", will reuse the +5 lines delta
    mov di, ( 7*80 + 15)*2      ; offset of [2+13, 2+5] character ([x,y])
    call FillCharOnly           ; CX+DX was preserved by the call, still same
    mov di, ( 7*80 + 28)*2      ; offset of [2+26, 2+5] character ([x,y])
    call FillCharOnly           ; CX+DX was preserved by the call, still same
    mov di, ( 7*80 + 41)*2      ; offset of [2+39, 2+5] character ([x,y])
    call FillCharOnly           ; CX+DX was preserved by the call, still same

    ; wait for some key hit, like enter
    mov ah,1
    int 21h
    ; exit to DOS correctly
    mov ax,4C00h
    int 21h

; helper subroutines

; al = character to fill with, di = starting offset, cx = count, dx = next offset delta
; Will write al to memory es:[di], advancing di by delta in dx, modifies di
FillCharOnly:
    push    cx                  ; preserve count
FillCharOnly_loop:
    mov     es:[di],al          ; store the character
    add     di,dx               ; advance di pointer
    dec     cx
    jnz     FillCharOnly_loop   ; repeat count-many times
    pop     cx                  ; restore count
    ret

; al = character to fill with, di = starting offset, cx = count
; Will write al to memory es:[di], advancing di by 2, modifies di and dx
; works as "horizontal line" filler in VGA text mode
FillCharOnly_horizontal:
    mov     dx,2
    jmp     FillCharOnly        ; continue with general subroutine

; al = character to fill with, di = starting offset, cx = count
; Will write al to memory es:[di], advancing di by 160, modifies di and dx
; works as "vertical line" filler in VGA text mode
FillCharOnly_vertical:
    mov     dx,160              ; next line in 80x25 mode is +160 bytes away
    jmp     FillCharOnly        ; continue with general subroutine

The extended VGA ASCII codes from here: https://en.wikipedia.org/wiki/Code_page_437 (notice what is 255 ... our favourite directory name when at high school, when we did want to make it "secret").

Hopefully that code is commented enough to be easy to understand, and it will give you some ideas how to save yourself some typing in assembly, and instead trade it off for some heave head scratching when the calculations don't go the way you expected... ;)

Also the emu8086 has built-in debugger, it's absolutely invaluable and essential tool. Every time you write some small part of your new code, jump into debugger, and single-step over each instruction, slowly and meticulously, checking all reported machine state changes (register values, flags, modified memory), and compare that with expected/assumed behaviour. Any discrepancy should be reasoned about and understood, resulting either into fix of code (to do what you did want), or adjusting your assumptions (to correctly understand what truly happens in the computer).

That also means, that you should invest heavily into time spend writing your source, to make sure that the written source is clearly stating your original intent, and is easy to read+understand. For example use long and meaningful label and variable names. Comment every section of code, what is its purpose.

Don't try to save your time on source writing, because that will usually cost you much more time upon source reading+debugging+fixing. The source should read well and should be understandable for human. Just passing through assembler is not enough, that mean it's readable for machine, but machine will not help you to fix it.


I did test the code with turbo assembler 4.1, command line used (ASM file named GAMEBOX.ASM (in dosbox):

tasm.exe /m5 /w2 /l GAMEBOX
tlink GAMEBOX.OBJ

With EXE target defined at the source beginning:

.model small
.stack 1000h

.data

.code
start:
    mov ax, @data               ; ds = data segment
    ...

For debugging in turbo debugger (TD.EXE), you should switch in Options -> Display options "Display swapping" to "Always", otherwise the direct video memory overwrites may be not visible on user screen (Alt+F5) (they will overwrite debugger's screen, and that one will refresh it immediately after).

Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • Thank you for such a great answer! I couldn't believe you wrote that much for me haha. Very informative and helpful. Thank you! I will try to implement these changes after I finish some basic functions :) – TheCoolest2 Nov 12 '17 at 20:52
  • Hello. So I moved all my data at the bottom, but I have a new problem. I added some string variables and when the program gets to the variables, if the string is a certain amount of characters, it says out of memory. If its less characters, it moves to the next line which is another string but the program never ends and eventually restarts. – TheCoolest2 Nov 13 '17 at 00:40
  • I know it isn't my method to go through the string to print the characters on screen, because I commented my method out and it still says out of memory. For example, the new variables are string1 db "[ENTER]- Select #" – TheCoolest2 Nov 13 '17 at 00:43
  • I have no idea who/what/why shows some out of memory, but I already answered you in this answer, let me repeat the most important part: *"Every time you write some small part of your new code, jump into debugger, and single-step over each instruction, slowly and meticulously, checking all reported machine state changes (register values, flags, modified memory), and compare that with expected/assumed behaviour."* (and add code by small chunks, so you can tell where it went downhill) ... that should give you good starting point for new question. Seeing "out of memory" in asm is very very weird. – Ped7g Nov 13 '17 at 01:03
  • @TheCoolest2 I mean, the wrong code usually will crash something, or whole machine, etc.. there's rarely anything to display "out of memory" which stays alive and operational enough.. unless it's some emu8086 environment message, then you somehow managed to put it under too much strain, but hard to tell what it was, without source. (if you are still targetting COM, doesn't your binary already hit the 64k limit? Sounds impossible to me with such simple app, but if you did something very anti-16b mode like reserving 40k buffer...) – Ped7g Nov 13 '17 at 01:04
  • Hello. That's what I'm currently doing, I will get back to you. Edit: Yes, it should hit the 64k limit. That's why I'm confused, I doubt I reached that limit. The only thing I added to yesterday's source was the data at the bottom. The other thing I added was changing numbers to ascii codes to display, but that isn't the issue, because I removed that code all together and still get the error with the string. I'm looking at documentation for variables currently with emu8086. – TheCoolest2 Nov 13 '17 at 01:09
  • @TheCoolest2 another main suspect would be stack overflow due to some unwanted infinite recursion. Verify in debugger your `sp` is stable over execution, going down/up only in expected manner (call and push will move it down, pop/ret should return back to upper values). – Ped7g Nov 13 '17 at 01:09
  • Hello. I just looked at that, it stood stable, unsigned value of 65534, as expected. – TheCoolest2 Nov 13 '17 at 01:13
  • Some very recent versions of emu8086 added listing file IIRC, or just check in debugger the symbol values (where the data lands - guess from that how large they are). Start of code is at 0x100 for COM file, and you can go up all the way to the 0xFFFF offset (although my suggested code did use stack for calls and push/pop, and the default stack is also in your main segment, going from top FFFE down, so it will overwrite your data if they reach too far toward end, and you push too much on stack. ... looks like you somehow reserved something BIG... (in 16b world even 20000 bytes is big) – Ped7g Nov 13 '17 at 01:15
  • I don't know if I'm bad at this but I don't see anything. Als, it works fine when I run with TASM. Edit: Okay nvm, it crashes – TheCoolest2 Nov 13 '17 at 01:42
  • Nope, that's completely normal when learning to code in asm, takes few years to get enough experience to write only few bugs, and spot most of them during few debugging sessions. Just keep some method in the madness, trying one thing after another, reasoning about everything, focusing on different parts one by one. Seeing the crash itself, where it happens with the invalid values in registers/memory is usually reasonable hint (although it may be very tricky to track down true origin of problem). Going back to last known source and writing the new code again, by even smaller chunks often helps. – Ped7g Nov 13 '17 at 02:04
  • So, in TASM, it says my string variables are "value out of range". I have to figure out why – TheCoolest2 Nov 13 '17 at 02:04
  • That's assembling error, not runtime... those should be quite obvious, if you re-read what you wrote, and understand all of it well. Don't you get it over some conditional jump? Don't be so vague, assembling errors report also number of line, so you can post exact message + line from editor. Runtime errors are much worse (to describe precisely in first place, and to find the reason after). – Ped7g Nov 13 '17 at 02:05
  • The line's are my two string variables as I wrote above stringOne dw '[ENTER] is' stringTwo dw 'testtest' No, these are after I draw the box corners. Not after a conditional jump* – TheCoolest2 Nov 13 '17 at 02:10
  • Define ASCII strings with `db` (as bytes), not `dw` (as words). ... Not sure, why the TASM picks that "value out of range" error message, as it could have also assembled that in UTF-16-like way using 16b letter codes, so you are sort of lucky it did catch you early on this one... (then again back in TASM era 16b wide chars were not very common, that came with win32 I think) ... thinking about it, and as the TASM can use `'AB'` as word value, it truly tried to calculate numeric value of all the letters and that didn't fit into 16b word. The `db` has special exception handling strings (n bytes) – Ped7g Nov 13 '17 at 02:12
  • Okay, I tried that earlier. I swear I did, but that seems to solve the run out of memory issue. I had another issue in TASM, but after research I read to not do mov ax, @data, and to make it ax, cs. In TASM, it works fine. In emu8086, it seems to not care. Also, I solved the the program getting stuck at the second declared variable stringTwo by putting a "ret" before all the variables. I read this is not needed though. Wouldn't this just make my game unplayable after I add it? Infact it really isn't a fix, it just a cheap way of ignoring the problem – TheCoolest2 Nov 13 '17 at 02:36