assemblystackx86-16local-variablessubroutine

How do I push the result on stack in a subroutine?


org 0x0100      
jmp start  

; Declare variables
input1: db 0  
input2: db 7  
input3: db 9  
input4: db 1  
message1: db 'The value of AX, BX and CX is: '
length: dw 31

start:
    ; Push output variables with random values
    push 3       ; Random value 1
    push 5       ; Random value 2
    push 7       ; Random value 3

    call my_subroutine

    ; Retrieve output variables
    pop cx       ; Retrieve output into CX
    pop bx       ; Retrieve output into BX
    pop ax       ; Retrieve output into AX

    ;Display the output values
    call display
    
    ; Exit program
    mov ax, 0x4c00
    int 0x21

my_subroutine:
    ; Save registers
    push ax
    push bx
    push si
    push di
    push cx

    ; Local variables (initialized with random values)
    local1: dw 1
    local2: dw 2
    local3: dw 3
    local4: dw 4
    local5: dw 5

    ; Example operations (just random logic for demonstration)
    mov ax, [input1] ; Load first input
    add ax, [local1] ; Add local variable
    mov bx, [input2] ; Load second input
    sub bx, [local2] ; Subtract local variable

    ; Push results
    ;push ax          ; Push first output (AX)
    ;push bx          ; Push second output (BX)
    ;mov cx, word [input3]      
    ;push cx          ; Push third output (local variable)

    ; Restore registers
    pop cx           ; Pop last pushed value into CX
    pop bx           ; Restore BX
    pop ax           ; Restore AX
    pop di           ; Restore DI
    pop si           ; Restore SI
    ret              ; Return to the caller

cls:

  push es
  push ax
  push di

  mov ax, 0xB800
  mov es, ax
  mov di, 0

 allSpace:

 mov word [es:di], 0x0720
 add di, 2
 cmp di, 4000
 jne allSpace

 pop di
 pop ax
 pop es

 ret

convert_num_to_string:
    push bp
    mov bp, sp 
    push es
    pusha
    mov ax, 0xb800
    mov es, ax
    mov ax, [bp+4]
    mov bx, 10
    mov cx, 0

 nextdigit: 
    mov dx, 0
    div bx
    add dl, 0x30
    push dx
    inc cx
    cmp ax, 0
    jnz nextdigit


    nextpos: 
    pop dx
    mov dh, 0x07
    mov [es:di], dx
    add di, 2
    loop nextpos

    popa
    pop es
    pop bp
    ret 2

display_message:
  push bp
  mov bp, sp
  push es
  push ax
  push bx
  push cx

  mov ax, 0xb800
  mov es, ax

  mov si, [bp+6]
  mov cx, [bp+4]
  mov ah, 0x07

  printAllChar:
  
  mov al, [si]
  mov [es:di], ax
  add di, 2
  add si, 1
  loop printAllChar

  pop cx
  pop bx
  pop ax
  pop es
  pop bp
  ret 4

display:  
    call cls
    mov di, 0                  
    push message1                  
    push word [length]
    call display_message
    
    mov di, 62 
    push ax           
    call convert_num_to_string

    mov di, 66
    push bx           
    call convert_num_to_string

    mov di, 70
    push cx           
    call convert_num_to_string


    ret



Main problem in my_subroutine:

In the subroutine above, when I push my result onto the stack, the control does not return to below call my_subroutine, but rather an infinite loop runs.

By the way, here is the question I am trying to solve:

You are required to write a subroutine in assembly language that:

  1. Takes 4 input variables (which will be the digits from your roll number i.e 22F-3440 , 3440 are the 4 inputs).
  2. Returns 3 output variables. These output variables are pushed (with random values) before the input variables in the main.
  3. Creates 5 local variables inside the function. Initialized with random values from 1 to 10.
  4. Uses the registers AX, BX, SI, DI, and CX to perform operations, but save and restore these registers before and after use to avoid overwriting their original values.
  5. The outputs must be popped into AX, BX, and CX after the subroutine returns.
  6. Display these to the console.

Solution

  • ; Local variables (initialized with random values)
    local1: dw 1
    local2: dw 2
    local3: dw 3
    local4: dw 4
    local5: dw 5
    

    These are not local variables! They're just as global as those other variables that you've declared (input, message, length). They only seem local because their name says so and because you've written them inside the my_subroutine subroutine. In many assemblers you could at least make these variables 'accessible local-only', by prepending their names with a dot: .local1: dw 1. But this is still not ideal because they will continue to occupy their memory for as long as your program runs.
    Additionally because of their emplacement in the execution path of your program, the cpu will execute these data bytes with possible disastrous results. In your main you knew that you had to jump around the data declarations with that jmp start. So, you could have used something similar here.
    Anyway, for your task the best solution is to place the local variables on the stack. See the code example below.

    ; Save registers
    push ax
    push bx
    push si
    push di
    push cx
    
    ...
    
    ; Restore registers
    pop cx
    pop bx
    pop ax
    pop di
    pop si
    ret
    

    You must restore the registers in the reverse order to how you pushed them before. Except for CX, this is not the case here!

    1. Returns 3 output variables. These output variables are pushed (with random values) before the input variables in the main.

    This is an important sentence and my take on it, is that a total of 7 values get pushed on the stack prior to calling the subroutine. Since unlike for the local variables, no limit was placed on the random values, you could save a few bytes and just push whatever (random) value there happens to be in the AX, BX, and CX registers. Alternatively, just consider the stack memory is already filled with such 'random' values, and thus a mere sub sp, 6 will reserve the space for those 3 outputs:

      ; Push output variables with random values
      push cx      ; (1)                 alternative:   sub sp, 6    ; (1)(2)(3)
      push bx      ; (2)
      push ax      ; (3)
      ; Push input variables with digits from roll number
      push 1       ; 4th digit
      push 9       ; 3rd digit
      push 7       ; 2nd digit
      push 0       ; 1st digit
      call my_subroutine
      pop  ax      ; (3)
      pop  bx      ; (2)
      pop  cx      ; (1)
    

    Stack layout once we're inside the subroutine:

    1  2  3  4  5  CX DI SI BX AX BP RET 0  7  9  1  ?  ?  ?
    ^  ^                          ^      ^  ^        ^  ^  ^
    |  [bp-18]                    [bp]   |  [bp+6]   |  |  [bp+16]
    [bp-20]                              [bp+4]      |  [bp+14]
                                                     [bp+12]
    
    my_subroutine:
      push bp
      mov  bp, sp
      push ax      ; Save registers
      push bx
      push si
      push di
      push cx
      push 5       ; Local variables (initialized with random values)
      push 4
      push 3
      push 2
      push 1
    
      ; Example operations (just random logic for demonstration)
      mov  ax, [bp+4]    ; Load first input
      add  ax, [bp-20]   ; Add local variable
      mov  bx, [bp+6]    ; Load second input
      sub  bx, [bp-18]   ; Subtract local variable
      ...
    
    
      mov  [bp+12], ax   ; "Push" results
      mov  [bp+14], bx
      mov  [bp+16], cx
      add  sp, 10        ; Release local variables
      pop  cx            ; Restore registers
      pop  di
      pop  si
      pop  bx
      pop  ax
      pop  bp
      ret  8             ; Remove the 4 inputs
    

    display_message currently forgets to preserve SI and DI, but needlessly preserves BX. Also it's a bit strange to see DI as a register-input to this routine together with a couple of stack-inputs. I think 3 stack-inputs OR 3 register-inputs would be nicer...