Author Topic: Why doesn't stuff work right in DOS EXEs, but does work right in my DOS COMs?  (Read 368 times)

Offline ben321

  • Full Member
  • **
  • Posts: 133
So I took my ASM code for a COM file, which I can create directly in NASM, making sure to start it with ORG 0x100 so that it has the offsets are calculated correctly for a COM file. And I then converted it to an EXE by first removing the ORG statement, and then adding statements to set up 3 sections (code section for the code, then data section for any embedded data in the EXE, and finally a stack section so DOS knows how much stack to allocate).

But there's a problem. The EXE isn't working right. In the case of my test program, it's supposed to display an image. The COM file works perfect for this. The EXE file only displays a corrupted picture. It appears that memory address 0xB8000 (segment 0xB800 offset 0), which SHOULD be the start of 4-color CGA graphics mode, is only the start of the graphics memory for COM files. It seems that when using a DOS EXE, the EXE loader in DOS rearranges the memory mapping for video RAM, so that VRAM no longer starts where it should in the memory address space. How do I fix this?

Offline ben321

  • Full Member
  • **
  • Posts: 133
I realized what was wrong now. I had assumed that the DOS EXE loader would have automatically read the data segment address from the EXE into the DS register, but (at least in DosBox, where I was testing my program) it didn't. I had to have my program set the DS register, using "mov ax,data" followed by "mov ds,ax". In COM files this isn't a problem, because everything is in a single segment, code and data.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2601
  • Country: us
Hi again ben321,

It has been a very long time since I did DOS files. I wouldn't have remembered whether the OS would set your segment registers or whether you have to do it, but you are correct. This stuff is "supposed to work" and I'm glad you got it to!

Best,
Frank




Offline fredericopissarra

  • Full Member
  • **
  • Posts: 228
  • Country: br
Ok... On MS-DOS, COM files follows the TINY memory model, where CS=DS=ES. For EXE files any model can be used, so, there are no garantees about DS and ES and they must be initialized. SS is initialized by DOS to use its stack (the linker will warn you: "No stack", but DOS stack is used!). Here's two small programs to show you the registers on entry:
Code: [Select]
; SHOW.ASM
;   This is a COM executable:
;
;   nasm -f bin show.asm show.com
;

  bits  16

%macro dword2hexstr 2
  lea   di,[%1]
  mov   eax,%2
  call  dw2hex
%endmacro

%macro word2hexstr 2
  lea   di,[%1]
  mov   ax,%2
  call  w2hex
%endmacro

  org   0x100

  ; Save modified registers
  mov  [cs:oldESP],esp
  mov  [cs:oldDS],ds

  push  cs
  pop   ds

  mov  [oldEBX],ebx
  mov  [oldEDI],edi

  ; Prepare string
  lea   di,[_eax]
  call  dw2hex

  dword2hexstr _ebx, [oldEBX]
  dword2hexstr _ecx, ecx
  dword2hexstr _edx, edx
  dword2hexstr _esi, esi
  dword2hexstr _edi, [oldEDI]
  dword2hexstr _ebp, ebp
  dword2hexstr _esp, [oldESP]

  word2hexstr  _cs, cs
  word2hexstr  _ds, [oldDS]
  word2hexstr  _es, es
  word2hexstr  _ss, ss
  word2hexstr  _fs, fs
  word2hexstr  _gs, gs

  lea   dx,[msg]
  mov   ah,9
  int   0x21

  mov   ax,0x4c00
  int   0x21

; Entry: DS:DI points to buffer,
;        AL = hexvalue to convert
; Destroys: BX, DI (advances)
b2hex:
  movzx bx,al
  shr   bx,4
  mov   bl,[bx+hextbl]
  mov   [di],bl
  inc   di
  movzx bx,al
  and   bx,0x0f
  mov   bl,[bx+hextbl]
  mov   [di],bl
  inc   di
  ret

w2hex:
  xchg  al,ah
  call  b2hex
  xchg  al,ah
  call  b2hex
  ret

dw2hex:
  push  eax
  shr   eax,16
  call  w2hex
  pop   eax
  call  w2hex
  ret

hextbl:
  db  `0123456789ABCDEF`

oldEBX: dd  0
oldEDI: dd  0
oldESP: dd  0
oldDS:  dw  0

; EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
; ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
; CS=0000 DS=0000 ES=0000 FS=0000 GS=0000

msg:  db  `EAX=`
_eax: db  `00000000`
      db  ` EBX=`
_ebx: db  `00000000`
      db  ` ECX=`
_ecx: db  `00000000`
      db  ` EDX=`
_edx: db  `00000000\r\n`
      db  `ESI=`
_esi: db  `00000000`
      db  ` EDI=`
_edi: db  `00000000`
      db  ` EBP=`
_ebp: db  `00000000`
      db  ` ESP=`
_esp: db  `00000000\r\n`
      db  `CS=`
_cs:  db  `0000`
      db  ` DS=`
_ds:  db  `0000`
      db  ` ES=`
_es:  db  `0000`
      db  ` SS=`
_ss:  db  `0000`
      db  ` FS=`
_fs:  db  `0000`
      db  ` GS=`
_gs:  db  `0000\r\n$`
Code: [Select]
; SHOWEXE.ASM
;
;  nasm -fobj showexe.asm -o showexe.obj
;  tlink -Tde showexe.obj,showexe.exe

  bits  16

%macro dword2hexstr 2
  lea   di,[%1]
  mov   eax,%2
  call  dw2hex
%endmacro

%macro word2hexstr 2
  lea   di,[%1]
  mov   ax,%2
  call  w2hex
%endmacro

  section _TEXT

..start:
  ; Save modified registers
  mov  [cs:oldESP],esp
  mov  [cs:oldDS],ds

  push  _DATA
  pop   ds

  mov  [oldEBX],ebx
  mov  [oldEDI],edi

  ; Prepare string
  lea   di,[_eax]
  call  dw2hex
  dword2hexstr _ebx, [oldEBX]
  dword2hexstr _ecx, ecx
  dword2hexstr _edx, edx
  dword2hexstr _esi, esi
  dword2hexstr _edi, [oldEDI]
  dword2hexstr _ebp, ebp
  dword2hexstr _esp, [oldESP]
  word2hexstr  _cs, cs
  word2hexstr  _ds, [oldDS]
  word2hexstr  _es, es
  word2hexstr  _ss, ss
  word2hexstr  _fs, fs
  word2hexstr  _gs, gs

  lea   dx,[msg]
  mov   ah,9
  int   0x21

  mov   ax,0x4c00
  int   0x21

; Entry: DS:DI points to buffer,
;        AL = hexvalue to convert
; Destroys: BX, DI (advances)
b2hex:
  movzx bx,al
  shr   bx,4
  mov   bl,[bx+hextbl]
  mov   [di],bl
  inc   di
  movzx bx,al
  and   bx,0x0f
  mov   bl,[bx+hextbl]
  mov   [di],bl
  inc   di
  ret

w2hex:
  xchg  al,ah
  call  b2hex
  xchg  al,ah
  call  b2hex
  ret

dw2hex:
  push  eax
  shr   eax,16
  call  w2hex
  pop   eax
  call  w2hex
  ret

oldESP: dd  0
oldDS:  dw  0

  section _DATA

hextbl:
  db  `0123456789ABCDEF`

oldEBX: dd  0
oldEDI: dd  0

; EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
; ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
; CS=0000 DS=0000 ES=0000 FS=0000 GS=0000

msg:  db  `EAX=`
_eax: db  `00000000`
      db  ` EBX=`
_ebx: db  `00000000`
      db  ` ECX=`
_ecx: db  `00000000`
      db  ` EDX=`
_edx: db  `00000000\r\n`
      db  `ESI=`
_esi: db  `00000000`
      db  ` EDI=`
_edi: db  `00000000`
      db  ` EBP=`
_ebp: db  `00000000`
      db  ` ESP=`
_esp: db  `00000000\r\n`
      db  `CS=`
_cs:  db  `0000`
      db  ` DS=`
_ds:  db  `0000`
      db  ` ES=`
_es:  db  `0000`
      db  ` SS=`
_ss:  db  `0000`
      db  ` FS=`
_fs:  db  `0000`
      db  ` GS=`
_gs:  db  `0000\r\n$`
Running both:
Code: [Select]
C:\Work> show
EAX=00000000 EBX=00000000 ECX=000000FF EDX=00000194
ESI=00000100 EDI=0000FFFE EBP=0000091C ESP=0000FFFE
CS=0192 DS=0192 ES=0194 SS=0194 FS=0000 GS=0000

C:\Work> showexe
EAX=00000000 EBX=00000000 ECX=000000FF EDX=00000194
ESI=00000000 EDI=00000000 EBP=0000091C ESP=C67C1818
CS=01A4 DS=C0C2 ES=0194 SS=01A4 FS=0000 GS=0000
Don't take for granted DS, ES or SS on EXE files. And don't take for granted SS on COM and EXE files.
And any other GPRs cannot be taken for granted as well.
« Last Edit: September 27, 2022, 02:55:43 PM by fredericopissarra »