{"id":24323,"date":"2007-12-03T10:00:00","date_gmt":"2007-12-03T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2007\/12\/03\/how-do-16-bit-programs-start-up\/"},"modified":"2007-12-03T10:00:00","modified_gmt":"2007-12-03T10:00:00","slug":"how-do-16-bit-programs-start-up","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20071203-00\/?p=24323","title":{"rendered":"How do 16-bit programs start up?"},"content":{"rendered":"<p>\nBack in 16-bit Windows,\nMS-DOS cast a long and dark shadow.\nThe really ugly low-level munging was very much in the MS-DOS spirit.\nYou opened files by setting up registers and issuing an <code>int 21h<\/code>,\njust like in MS-DOS.\nAlthough the interrupt went to Windows instead,\nWindows maintained the MS-DOS calling convention.\nProcess startup followed the same\n&#8220;real men write in assembly language&#8221; philosophy.\n<\/p>\n<p>\nAll the parameters to a 16-bit program\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/12\/04\/1205831.aspx#1240457\">\nwere passed in registers<\/a>.\nThe entry point to a 16-bit process received the following\nparameters on Windows&nbsp;3.1:\n<\/p>\n<table ALIGN=\"CENTER\">\n<tr>\n<td>AX<\/td>\n<td>zero\n       (used to contain even geekier information in Windows&nbsp;2)<\/td>\n<\/tr>\n<tr>\n<td>BX<\/td>\n<td>stack size<\/td>\n<\/tr>\n<tr>\n<td>CX<\/td>\n<td>heap size<\/td>\n<\/tr>\n<tr>\n<td>DX<\/td>\n<td>unused (reserved)<\/td>\n<\/tr>\n<tr>\n<td>SI<\/td>\n<td>previous instance handle<\/td>\n<\/tr>\n<tr>\n<td>DI<\/td>\n<td>instance handle<\/td>\n<\/tr>\n<tr>\n<td>BP<\/td>\n<td>zero (for stack walking)<\/td>\n<\/tr>\n<tr>\n<td>DS<\/td>\n<td>application data segment<\/td>\n<\/tr>\n<tr>\n<td>ES<\/td>\n<td>selector of program segment prefix<\/td>\n<\/tr>\n<tr>\n<td>SS<\/td>\n<td>application data segment (SS=DS)<\/td>\n<\/tr>\n<tr>\n<td>SP<\/td>\n<td>top of stack<\/td>\n<\/tr>\n<\/table>\n<p>\nHey, nobody said that 16-bit Windows was designed for portability.\n<\/p>\n<p>\nThe first thing a 16-bit program did was call the\n<code>InitTask<\/code> function.\nThis function receives its parameters in registers,\nprecisely in the format that they are received by the program\nentry point.\nThe <code>InitTask<\/code> function initializes the stack,\nthe data segment, the heap,\nretrieves and prepares the command line,\nrecovers the <code>nCmdShow<\/code> parameter that was passed\nto <code>WinExec<\/code>, all the normal startup stuff.\nIt even edits the stack of the caller so that real-mode stack\nwalking works (critical for memory management in real-mode).\nWhen <code>InitTask<\/code> is all finished,\nit returns with the registers set for the next phase:\n<\/p>\n<table ALIGN=\"CENTER\">\n<tr>\n<td>AX<\/td>\n<td>selector of program segment prefix (or 0 on error)<\/td>\n<\/tr>\n<tr>\n<td>BX<\/td>\n<td>offset of command line<\/td>\n<\/tr>\n<tr>\n<td>CX<\/td>\n<td>stack limit<\/td>\n<\/tr>\n<tr>\n<td>DX<\/td>\n<td><code>nCmdShow<\/cODE><\/td>\n<\/tr>\n<tr>\n<td>SI<\/td>\n<td>previous instance handle<\/td>\n<\/tr>\n<tr>\n<td>DI<\/td>\n<td>instance handle<\/td>\n<\/tr>\n<tr>\n<td>BP<\/td>\n<td>top of stack (for stack walking)<\/td>\n<\/tr>\n<tr>\n<td>DS<\/td>\n<td>application data segment<\/td>\n<\/tr>\n<tr>\n<td>ES<\/td>\n<td>selector of command line<\/td>\n<\/tr>\n<tr>\n<td>SS<\/td>\n<td>application data segment (SS=DS)<\/td>\n<\/tr>\n<tr>\n<td>SP<\/td>\n<td>edited top of stack<\/td>\n<\/tr>\n<\/table>\n<p>\nOnce <code>InitTask<\/code> returns, the stack, heap, and data segment\nare \"ready to run,\" and if you have no other preparations to do,\nyou can head right for the application's <code>WinMain<\/code> function.\nMinimal startup code therefore would go like this:\n<\/p>\n<pre>\n    call    far InitTask\n    test    ax, ax\n    jz      exit\n    push    di      ; hInstance\n    push    si      ; hPrevInstance\n    push    es      ; lpszCmdLine selector\n    push    bx      ; lpszCmdLine offset\n    push    dx      ; nCmdShow\n    ... some lines of code that aren't important to the discussion ...\n    call    far WinMain ; call the application's WinMain function\n    ; return value from WinMain is in the AL register,\n    ; conveniently positioned for the exit process coming up next\nexit:\n    mov     ah, 4Ch ; exit process function code\n    int     21h     ; do it\n<\/pre>\n<p>\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/12\/04\/1205831.aspx#1206443\">\nWhy wasn't the application entry point called <code>main<\/code>?<\/a>\nWell, for one thing, the name <code>main<\/code> was already taken,\nand Windows didn't have the authority to reserve an alternate definition.\nThere was no C language standardization committee back then;\nC was what\n<a HREF=\"http:\/\/netlib.bell-labs.com\/who\/dmr\/\">\nDennis<\/a> said it was,\nand it was hardly guaranteed that Dennis would take any special steps to\npreserve Windows source code\ncompatibility in any future version of the C language.\nSince K&amp;R didn't specify that implementations could extend the\nacceptable forms of the <code>main<\/code> function,\nit was entirely possible that there was a legal C compiler that\nrejected programs that declared <code>main<\/code> incorrectly.\nThe current C language standard explicitly permits implementation-specific\nalternate definitions for <code>main<\/code>, but\nrequiring all compilers to support\nthis new Windows-specific version\nin order to compile Windows programs\nwould gratuitously restrict the set of compilers you could use for\nwriting Windows programs.\n<\/p>\n<p>\nIf you managed to overcome that obstacle,\nyou'd have the problem that the Windows version of <code>main<\/code>\nwould have to be something like this:\n<\/p>\n<pre>\nint main(int argc, char *argv[], HINSTANCE hinst,\n         HINSTANCE hinstPrev, int nCmdShow);\n<\/pre>\n<p>\nDue to the way C linkage was performed,\nall variations of a function had to agree on the parameters\nthey had in common.\nThis means that the Windows version would have to add its parameters\nonto the end of the longest existing version of <code>main<\/code>,\nand then you'd have to cross your fingers and hope that the C language\nnever added another alternate version of main.\nIf you went this route, your crossed fingers failed you, because\nit turns out that\n<a HREF=\"http:\/\/qnxcs.unomaha.edu\/help\/product\/neutrino\/lib_ref\/m\/main.html\">\na third parameter was added to <code>main<\/code>\nsome time later<\/a>,\nand it conflicted with your Windows-friendly version.\n<\/p>\n<p>\nSuppose you managed to convince Dennis not to allow that three-parameter\nversion of <code>main<\/code>.\nYou still have to come up with those first two parameters,\nwhich means that every program's startup code needs to contain\na command line parser.\nBack in the 16-bit days, people scrimped to save every byte.\nTelling them, \"Oh, and all your programs are going to be 2KB bigger\"\nprobably wouldn't make you a lot of friends.\nI mean, that's four sectors of I\/O off a floppy disk!\n<\/p>\n<p>\nBut probably the reason why the Windows entry point was given a\ndifferent name\nis to emphasize that it's a different execution environment.\nIf it were called <code>main<\/code>,\npeople would take C programs designed for a console environment,\nthrow them into their Windows compiler, and then run them,\nwith disastrous results.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Back in 16-bit Windows, MS-DOS cast a long and dark shadow. The really ugly low-level munging was very much in the MS-DOS spirit. You opened files by setting up registers and issuing an int 21h, just like in MS-DOS. Although the interrupt went to Windows instead, Windows maintained the MS-DOS calling convention. Process startup followed [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-24323","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Back in 16-bit Windows, MS-DOS cast a long and dark shadow. The really ugly low-level munging was very much in the MS-DOS spirit. You opened files by setting up registers and issuing an int 21h, just like in MS-DOS. Although the interrupt went to Windows instead, Windows maintained the MS-DOS calling convention. Process startup followed [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/24323","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=24323"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/24323\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=24323"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=24323"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=24323"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}