title: How to unload .EXE residential program from memory
body: <p>I have a program that changes 1Ch int procedure and loads itself as residential. And it has .EXE structure (I know that .COM is preferable), but I need .EXE.The thing I have program that shows MCB blocks information. And after installing new 1Ch int procedure I can see that one new MCB block appeared and it related to my residential program. But after I tried to free memory (in my opinion unload resident) the MCB block related to residential program remained. My .EXE structure looks like this: stack segment->code segment(some data here too). And I save PSP addres before load and when I try to unload program I get stadart 1Ch int procedure back an put saved PSP address in ES then I call 49h of int 21h.
  So how can I fully unload .EXE residential program?</p>
  
  <p>UPD: I did some coding and have code that works, but when i'm trying to debug it in AFDPRO after first start resident loads fine bun the when i'm trying to load in debugger other program, like that shows MCB blocks, debugger crashes. Here is a code:</p>
  
  <pre><code>    ASTACK segment stack
  dw 100h dup('*')
  END_OF_STACK_MARK = $
  ASTACK ends
  
  ACODE segment
  assume CS: ACODE, DS: ACODE, SS: ASTACK
  
  NEW_INT_PROC proc far
          push DS                     ;Saving registers
          push ES
          push AX
          push BX
          push CX
          push DX
          push SI
          push DI
  
          mov AX, ACODE               ;Setting CODE seg to ds to
          mov DS, AX                  ;correctly accessing residential data
          xor AX, AX
  
          cmp val_COUNTER, 10
          je CLEAR_COUNTER
          mov AL, val_COUNTER
          add AL, '0'
          inc val_COUNTER
          call DIRECT_PRINT
          jmp EXIT_RES
      CLEAR_COUNTER:
          mov val_COUNTER, 0
      EXIT_RES:
          pop DI                      ;Recovering registers
          pop SI
          pop DX
          pop CX
          pop BX
          pop AX
          pop ES
          pop DS
  
          mov AL, 20h
          out 20h, AL
          iret
  NEW_INT_PROC endp
  
  DIRECT_PRINT proc near
      push AX
      push BX
      push CX
      push DX
      push SP
      push BP
      push SI
      push DI
      ;Getting current cursor position
      mov AH, 03h
      mov BH, 00h
      int 10h
      push DX;Saving current row and collumn of cursor position
      ;Settin new cursor position
      mov AH, 02h
      mov BH, 00h
      mov DX, 0000h
      int 10h
      ;Print number from AL
      mov AH, 09h
      mov BH, 0
      ;mov BL, 153
      mov CX, 1
      int 10h
      ;Recovering cursor position
      mov AH, 02h
      mov BH, 00h
      pop DX;Recoveing initial cursor position
      int 10h
      pop DI
      pop SI
      pop BP
      pop SP
      pop DX
      pop CX
      pop BX
      pop AX
      ret
  DIRECT_PRINT endp
  
  str_MOD db 'StdMod$'        ;\  Residential data (ResMod - if residential program, StdMod - if not residential program) 
  val_COUNTER db 0            ; | 
  val_RES_PSP_ADDR dw 0       ; | PSP address of residential programm
  val_OLD_INT_PROC_ADDR dd 0  ;/  Old int 1Ch address
  
  NEW_INT_PROC_MEM_MARK = $
  
  PRNT_MARKED_STRING proc near
      ;Print string with end of string mark
      ;String offset must be in DX
      push AX
      mov AH, 09h
      int 21h
      pop AX
      ret
  PRNT_MARKED_STRING endp
  
  LOAD_RESIDENT proc near
      push AX
      push BX
      push DX
  
      mov [str_MOD], 'R'
      mov [str_MOD + 1], 'e'
      mov [str_MOD + 2], 's'
      mov [val_RES_PSP_ADDR], ES              ;Save PSP address to realise memory later
  
      push ES
      mov AX, 351Ch                       ;Getting CS:IP of standart interruption procedure
      int 21h                             
      mov word ptr [val_OLD_INT_PROC_ADDR], BX    ;IP of standart interruption procedure saved 
      mov word ptr [val_OLD_INT_PROC_ADDR + 2], ES ;CS of standart interruption procedure saved
      pop ES
  
      push DS                             
      mov DX, offset NEW_INT_PROC     ;Installing new interruption on 1Ch
      mov AX, seg NEW_INT_PROC
      mov DS, AX
      mov AX, 251Ch
      int 21h
      pop DS 
  
      pop DX
      pop BX
      pop AX
  
      ret
  LOAD_RESIDENT endp
  
  FREE_RESIDENT proc near
      push AX
      push BX
      push DX
  
      push ES
      mov AX, 351Ch
      int 21h
      mov AX, word ptr ES:[val_OLD_INT_PROC_ADDR]
      mov word ptr [val_OLD_INT_PROC_ADDR], AX
      mov BX, word ptr ES:[val_OLD_INT_PROC_ADDR + 2]
      mov word ptr [val_OLD_INT_PROC_ADDR + 2], BX
      mov ES, ES:[val_RES_PSP_ADDR]
      push ES
      mov ES, ES:2Ch
      mov AH, 49h
      int 21h
      pop ES
      mov AH, 49h
      int 21h
      pop ES
  
      cli                                 ;Recovering standart interruption
      push DS 
      mov DX, word ptr [val_OLD_INT_PROC_ADDR]
      mov AX, word ptr [val_OLD_INT_PROC_ADDR + 2]
      mov DS, AX
      mov AX, 251Ch
      int 21h
      pop DS 
      sti
  
      pop DX
      pop BX
      pop AX
      ret
  FREE_RESIDENT endp
  
  IS_RES_RUNNING proc near
          push ES
          mov AX, 351Ch
          int 21h
          cmp ES:[str_MOD], 'R'
          jne EXIT_RES_NOT_RUNNING
          cmp ES:[str_MOD+1], 'e'
          jne EXIT_RES_NOT_RUNNING
          cmp ES:[str_MOD+2], 's'
          jne EXIT_RES_NOT_RUNNING
          cmp ES:[str_MOD+3], 'M'
          jne EXIT_RES_NOT_RUNNING
          cmp ES:[str_MOD+4], 'o'
          jne EXIT_RES_NOT_RUNNING
          cmp ES:[str_MOD+5], 'd'
          jne EXIT_RES_NOT_RUNNING
          mov [val_RES_RUNNING], 1
      EXIT_RES_NOT_RUNNING:
          pop ES
          ret
  IS_RES_RUNNING endp
  
  USER_UNLOAD proc near
          push ES
          push AX
          push BX
          mov AH, 62h
          int 21h
          mov ES, BX
          cmp byte ptr ES:[80h], 04h
          jne EXIT_USR_INLOAD
          cmp byte ptr ES:[82h], '/'
          jne EXIT_USR_INLOAD
          cmp byte ptr ES:[83h], 'u'
          jne EXIT_USR_INLOAD
          cmp byte ptr ES:[84h], 'n'
          jne EXIT_USR_INLOAD
          mov [val_RES_USR_UNLOAD], 1
      EXIT_USR_INLOAD:
          pop BX
          pop AX
          pop ES
          ret
  USER_UNLOAD endp
  
  str_RES_LOADED db 'Residential program has been loaded', 10, 13, '$'
  str_RES_RUNNING db 'Residential program is running', 10, 13, '$'
  str_RES_NOT_RUNNING db 'Residential program is not running', 10, 13, '$'
  str_RES_UNLOADED db 'Residential program has been unloaded', 10, 13, '$'
  val_RES_RUNNING db 0
  val_RES_USR_UNLOAD db 0
  
  MAIN proc far 
          sub AX, AX
          mov AX, ACODE
          mov DS, AX
  
          call IS_RES_RUNNING
          cmp val_RES_RUNNING, 1
          je FREE_RESIDENT_PROG
          mov DX, offset str_RES_NOT_RUNNING
          call PRNT_MARKED_STRING
          mov DX, offset str_RES_LOADED
          call PRNT_MARKED_STRING
          call LOAD_RESIDENT
          mov DX, offset NEW_INT_PROC_MEM_MARK
          add DX, offset END_OF_STACK_MARK
          add DX, 10Fh
          mov CL, 4
          shr DX, CL
          mov AX, 3100h
          int 21h
      FREE_RESIDENT_PROG:
          mov DX, offset str_RES_RUNNING
          call PRNT_MARKED_STRING
          call USER_UNLOAD
          cmp val_RES_USR_UNLOAD, 1
          jne EXIT
          mov DX, offset str_RES_UNLOADED
          call PRNT_MARKED_STRING
          call FREE_RESIDENT
      EXIT:
          mov AX, 4C00h
          int 21h
  MAIN endp
  ACODE ends
  end MAIN
  </code></pre>