{"id":97386,"date":"2017-11-13T07:00:00","date_gmt":"2017-11-13T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=97386"},"modified":"2019-03-13T01:20:13","modified_gmt":"2019-03-13T08:20:13","slug":"20171113-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20171113-00\/?p=97386\/","title":{"rendered":"On memory allocations larger than 64KB on 16-bit Windows"},"content":{"rendered":"<p>Allocating memory blocks larger than 64<a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=17933\">KB<\/a> was tricky in 16-bit Windows because the nature of 16-bit segment:offset addressing meant that you could access the memory only 64KB at a time. Global memory allocations returned you a segment (or selector, if running protected mode Windows), and the memory started at offset zero in that selector. Things got complicated once you needed to read the byte that comes after offset <code>0xFFFF<\/code>. <\/p>\n<p>For the purpose of discussion, let&#8217;s say that the value returned from <code>Global&shy;Alloc<\/code> was <code>0x1234<\/code>. The first 64KB of the allocated memory are accessible as <code>1234:0000<\/code> through <code>1234:FFFF<\/code>. <\/p>\n<p>In real mode, linear addresses are calculated by taking the segment number, multiplying by 16, and adding the offset. This means that <code>1234:0000<\/code> refer to linear byte <code>0x12340<\/code>, and <code>1234:FFFF<\/code> refer to linear byte <code>0x12340<\/code> + <code>0xFFFF<\/code> = <code>0x2233F<\/code>. The next linear byte is <code>0x22340<\/code>, which you could access as <code>2234:0000<\/code>. <\/p>\n<p>Conclusion: When the offset wraps around, you add <code>0x1000<\/code> to the segment. <\/p>\n<p>In standard mode, linear addresses are  calculated by looking up the base address of the selector in the descriptor table, and adding the offset. When Windows allocated a block larger than 64KB, it allocated a block of consecutive selectors, so that the first selector pointed to the first 64KB of the allocated memory, the second selector pointed to second 64KB of the allocated memory, and so on. <\/p>\n<p>Now, consecutive selectors do not have consecutive values, however. On the 80286, the bottom three bits of the selector are used for other purposes, so the numeric difference between consecutive selectors is actually 8. The first 64KB of the allocated memory are accessible as <code>1234:0000<\/code> through <code>1234:FFFF<\/code>, and the next byte after that is available as <code>123C:0000<\/code>. <\/p>\n<p>This makes for a bit of trouble if you&#8217;re writing a program that needs to run in both real mode and protected mode. When you reach the end of the first 64KB block, how much do you increment the segment\/selector by to reach the next 64KB block? <\/p>\n<p>Enter the <code>__AHINCR<\/code> variable. <\/p>\n<p>The <code>__AHINCR<\/code> variable is a variable exported from <code>KERNEL<\/code>. In real mode Windows, the value is <code>0x1000<\/code>. In protected mode Windows, the value is <code>0x0008<\/code>. When your program reaches the end of a 64KB block, it uses the <code>__AHINCR<\/code> value to decide how much to increment the segment\/selector by in order to reach the next 64KB block. <\/p>\n<p>Most programmers never saw this variable. It was hidden inside the code generated by the compiler. <\/p>\n<p>With the introduction of enhanced mode Windows, the memory manager did a little more. Enhanced mode Windows used the 80386, &#8220;Now with 32-bit registers!&#x2728;&#8221; This means that the offset portion of a selector:offset address can be a 32-bit value. <\/p>\n<p>The Windows memory manager assigned the selectors to the different 64KB chunks of data in the same way that the standard mode memory manager did, but instead of setting the selector limit to <code>0xFFFF<\/code>, it set the selector limit to extend to the entire remainder of the block. The first selector&#8217;s limit was the entire memory block. The second selector&#8217;s limit was the memory block minus 64KB. The third selector&#8217;s limit was the memory block minus 128KB. And so on until all the selectors were exhausted. <\/p>\n<p>This arrangement meant that if you could convince your compiler to do it (or if you wrote code in assembly language directly), <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20170927-00\/?p=97095#comment-1310625\">you could leave the selector alone and operate solely on the offset portion<\/a>. <\/p>\n<p>Windows 95 took advantage of this. The languages team <a HREF=\"https:\/\/technet.microsoft.com\/en-us\/library\/2008.09.windowsconfidential.aspx\">produced a special version of the compiler<\/a> that, with proper coaxing and appeasement, could be convinced to access memory using 32-bit offsets from a 16-bit selector, provided you declared the selector and the pointer in just the right way. <\/p>\n<p>No lesson today. Just some reminiscing. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>The mysterious __AHINCR.<\/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-97386","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>The mysterious __AHINCR.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97386","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=97386"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97386\/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=97386"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=97386"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=97386"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}