I posted an answer over at StackOverflow about someone wanting to decompile a 16-bit DOS executable. A few days later, someone else (inadvertently) made a comment that .EXE files couldn't be 104 bytes. Knowing that this can't be the case, since DOS .EXE file headers are only 28 bytes in size.
Ya, you guessed it, I had to try.
So I hard coded--word for word--a small .EXE file to print Hello, World! to the screen.
The .EXE is 59 bytes, total. This includes the 28 byte header. (Assembled with NBASM)
If we were to just return to DOS, not doing anything, the minimum file size would be 33 bytes, assuming a 'retf' will return to DOS.
However, there are some assumptions here. 1) There is no relocation table which may or may not be required depending on where you read the specification, and 2) The CRC is zero, which is considered okay in most cases.
Here is the source:
STACK_SIZE equ 128 .model tiny .code outfile 'small.exe' ; exe header org 00h dw 0x5A4D ; EXE signature dw (end_code & 511) ; bytes in last page dw ((end_code + 511) / 512) ; number of 512-byte pages dw 0 ; number of relocation entries dw ((start + 15) / 16) ; header size dw (((end - start) + 15) / 16) ; memory required (in paragraphs) dw (((end - start) + 15) / 16) ; memory requested (in paragraphs) dw 0x0000 ; ss (relocation) dw ((stack - start) + STACK_SIZE) ; sp dw 0 ; crc dw (start - start) ; initial ip dw 0x0000 ; cs (relocation) dw ? ; relocation table offset dw ? ; overlay dw ? ; overlay man ; this is where the relocation table would start. ; however, since we don't have one, let's start ; the code here instead ; executable start org 32 start: push cs ; make ds = cs pop ds ; mov ah,09h ; DOS String Out service mov dx,(string - start) ; must subtract origin int 21h ; initiate the service call mov ah,4Ch ; exit to DOS int 21h ; (no error value) string db 'Hello, World!', '$' end_code: orgnf (($ + 1) & ~1) ; align on a word boundary without stack dup STACK_SIZE,? ; adding a byte to the file ; end of our code/data/file end: .end
At 59 bytes, I believe this is the smallest an .EXE can be, using the following rules.
1) Must be a standard DOS 16-bit .EXE
2) Must use DOS service 09h to print the string 'Hello, World!' (exactly as shown)
3) Must use DOS service 4Ch to exit
If you can do better, please let me know.