2

I'm testing some memory dumping routines in 8086 Assembly based on the one that Keith of Chibiakumas created for displaying registers and showing memory. This routine is a modified version of his that shows the memory in big-endian 16-bit hexadecimal words to make the call stack easier to read. (The reason I was doing this was to figure out exactly what an INT does to the stack, but that's not what I'm asking about here.)

My understanding was that the stack on x86-based CPUs grew downward in memory, that is, as more values are pushed onto the stack, the new value is placed earlier in memory than the last. For example, let's pretend that SP = 0x03FA and the last word pushed onto the stack is 0x021F. If the word 0xFFFF is pushed onto the stack, then SP now equals 0x03F8 and the value 0xFFFF is stored at 0x03F8 and 0x021F is stored at 0x03FA.

This is where I'm running into some weirdness, and I can't tell if my code is faulty or my assumptions about the stack are wrong. The image below represents the output of the memory dump code, which shows the contents of each register. Once that returns, I then call a routine then performs a hexdump of BX words from ES:BP. The stack segment register SS was copied into BP, then I chose 0x03D0 for the offset (since it was close to the starting stack pointer value of 0x0400 and chose to dump 32 words. I did this twice, but before doing it the second time, I loaded 0xFFFF into AX and pushed AX four times. I was thinking that four 0xFFFFs would be lower in memory than the lowest value in the stack's memory.

But what I found was the opposite; it appears that new values on the stack are placed at the end, and forcing the others down. (Which as you probably know takes MUCH more work than simply overwriting new values.) Yet, the stack pointer decreased from the first and second register dumps, just as I expected. How can this be?

TL;DR: I can't tell if my code is bugged or if the stack doesn't grow downward after all.

The code for my main program, and the memory dump code (the register display code is correct, I'm sure). The picture below the code is a screenshot of the code's output.

    mov ax,@data        ;Get address of data segment
    mov ds,ax           ;load it into DS
    
    mov ax,@code        ; uncomment this to load ES with the code segment. 
    mov es,ax           ;       otherwise it's loaded with the data segment
    
    
    cld                 ;String functions are set to auto-increment
    
    mov ax,2            ;clear screen by setting video mode to 0
    int 10h             ;select text mode - We're already in it, so this clears the screen

    call doMonitor      ;show registers
    call NewLine        ;advances text cursor to next line by printing CR followed by LF
    call NewLine

    mov ax,ss
    mov es,ax           ;load stack segment into ES
    mov bp,03D0h        ;load an offset close to SP's current value
    mov bx,20h          ;dump enough bytes to see the stack
    call doMemDump16    ;dump 32 words starting at 0x03D0
    call NewLine
    call NewLine
    
    mov ax,0FFFFh
    push ax
    push ax
    push ax
    push ax             ;push 0xFFFF onto the stack four times, so we can see the stack move
                        ;   next time we dump the memory

    call doMonitor      ; show the registers again, so that we can see SP change
    call NewLine
    call NewLine

    mov ax,ss
    mov es,ax
    mov bp,03D0h
    mov bx,20h
    call doMemDump16

    mov ax,4C00h
    int 21h               ;return to DOS

;;;;;;;;;;;;;;;;;;;;;  memory dump routine (some subroutines were omitted, those are working fine)
doMemDump16:
    push ax
    push bx
    push cx 
            mov ax,es           ;PRINT THE SEGMENT:OFFSET POINTED TO BY ES:BP
            mov al,ah
            call Printhex
            mov ax,es
            call Printhex
            mov al,':'
            call PrintChar
            mov ax,bp
            mov al,ah
            call Printhex
            mov ax,bp
            call Printhex
            call NewLine
            
MemDump16Again: 
            mov ax, [es:bp]         ;read in a word from [ES:BP]
                xchg ah,al          ;print high byte first
                call Printhex
                xchg ah,al          ;then low byte
                call Printhex
            mov al,' '
            call Printchar          ;print a space between words
            inc bp
            inc bp                  ;next word
            dec bx                  ;decrease counter
            mov al,bl               ;we need to check whether counter is a multiple of 8 non-destructively
                and al,00000111b    ;filter by 8s on MS-DOS
            jne MemDump16Again      
            
            call NewLine            ;new line every 8 words printed
            cmp bx,0                ;is this the end of the specified data stream?
            jne MemDump16Again      ;if not, keep dumping
            
    pop cx
    pop bx
    pop ax
    
    ret

stack dump

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
puppydrum64
  • 1,598
  • 2
  • 15
  • 5
    The initial `SP` is `03FA` and the new `SP` is `03F2` which is 8 bytes less. So yes, stack did grow down by the 8 bytes you pushed. You are just dumping unrelated memory under the caller's stack pointer which contains the locals of your `doMemDump16` and its children. They were not moved anywhere, the `call doMemDump16` puts them there every time. Notice the first value under the `FFFF` is `004F` which is the return address and the next is `021F` which is the result of `push ax`. – Jester Dec 09 '21 at 22:55
  • Oh, I see now. The 021F was what I loaded into AX and the 0020 was what I loaded into BX prior to calling ```doMemDump16``` – puppydrum64 Dec 10 '21 at 02:06

0 Answers0