{"id":93261,"date":"2016-04-04T07:00:00","date_gmt":"2016-04-04T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=93261"},"modified":"2019-03-13T11:01:36","modified_gmt":"2019-03-13T18:01:36","slug":"20160404-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160404-00\/?p=93261","title":{"rendered":"Getting MS-DOS games to run on Windows 95: Working around the iretd problem"},"content":{"rendered":"<p>Today&#8217;s story is the story of <a HREF=\"https:\/\/archive.org\/details\/msdos_Speed_Racer_in_The_Challenge_of_Racer_X_1992\"><i>Speed Racer in the Challenge of Racer X<\/i><\/a>. Here goes. The really scary thing is that <i>I still remember the details<\/i>. <\/p>\n<p>To this day, I can&#8217;t bear to listen to the Speed Racer theme song because I spent over a week debugging why the program froze up right after the title sequence music. The crashes were completely nonsensical and random. <\/p>\n<p>Windows 95 uses the <code>iretd<\/code> instruction to return from the kernel back to the application. After days of frustrating head-scratching, I eventually discovered that if you use the instruction to return from the kernel back to the application, and the application is running 32-bit protected-mode code on a 16-bit stack, then only the bottom 16 bits of the <code>esp<\/code> register are updated by the <code>iretd<\/code> instruction. The upper 16 bits remain unchanged and continue to hold the value they had while you were in kernel mode. This behavior doesn&#8217;t appear to be documented anywhere in Intel&#8217;s reference books.&sup1; <\/p>\n<p>The effect of this is that 32-bit protected-mode code running on a 16-bit stack will observe that the upper 16 bits of the <code>esp<\/code> register are spontaneously corrupted randomly. (<a HREF=\"https:\/\/technet.microsoft.com\/en-us\/magazine\/jj203546.aspx\">Sound familiar<\/a>?) Unfortunately, <i>Speed Racer<\/i> was counting on the upper 16 bits of the <code>esp<\/code> register remaining zero. <\/p>\n<p>To fix this, I had to counter insanity with more insanity. <\/p>\n<p>At the last moment before restoring all the general purpose registers and executing the <code>iretd<\/code> instruction, Windows 95 does a check to see whether the troublesome scenario is about to occur. If so, the kernel sets up a temporary stack selector whose base linear address matches the high 16 bits of the kernel <code>esp<\/code> register, then switches to that stack while simultaneously zeroing out the high 16 bits of its own <code>esp<\/code> register. This double-switch rewrites the <code>ss:esp<\/code> value such that it points to the same memory, but shuffles the bits around to arrange for the high 16 bits of <code>esp<\/code> to be zero. In other words, it rewrote <code>SS:ESP = 00000000 + xxxxyyyy<\/code> as <code>SS:ESP = xxxx0000 + 0000yyyy<\/code>. (<a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">Sound familiar<\/a>?) <\/p>\n<p>At this point, the kernel is set up to restore the general purpose registers and perform the <code>iretd<\/code>. This returns control back to the application with the high 16 bits of the <code>esp<\/code> register set to zero, as the application expects. <\/p>\n<p>Now, this may seem like an awful lot of work just to get a single game to work, and it&#8217;s not like <i>Speed Racer<\/i> was a blockbuster game like <i>DOOM<\/i>. However, this particular problem was not intrinsic to <i>Speed Racer<\/i>. Rather, it was a problem in the client-side library code that came with the MS-DOS extender they were using, and that MS-DOS extender was one of the major players in the MS-DOS extender market, so fixing this issue actually fixed a lot of programs. It&#8217;s just that <i>Speed Racer<\/i> was the first one discovered to exhibit the problem, so it was the one I ended up debugging. <\/p>\n<p>&sup1;Maybe I&#8217;m missing it. <a HREF=\"http:\/\/tptp.cc\/mirrors\/siyobik.info\/instruction\/IRET%252FIRETD.html\">You tell me if you see it in there<\/a>. The pseudocode at the <code>RETURN-TO-OUTER-PRIVILEGE-LEVEL<\/code> label talks about raising an exception if the stack doesn&#8217;t have at least 8 bytes of data in it, but it doesn&#8217;t appear to discuss what happens to the <code>esp<\/code> register. The discussion says &#8220;If the return is to another privilege level, the IRET instruction also pops the stack pointer and SS from the stack,&#8221; but it doesn&#8217;t mention what happens if the destination stack pointer is a different size from the current stack pointer. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Speed Racer in the Challenge of Racer X, please make it stop.<\/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-93261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Speed Racer in the Challenge of Racer X, please make it stop.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93261","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=93261"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93261\/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=93261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=93261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=93261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}