home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 15 / CDACTUAL15.iso / cdactual / program / asm / LOADAL.ZIP / HYPRSPAC.DOC < prev    next >
Encoding:
Text File  |  1990-05-31  |  40.5 KB  |  892 lines

  1.  
  2.  
  3.  
  4.                            THE LOW-DOWN ON LOADALL:
  5.                             EXCERPTS FROM THE BOOK
  6.  
  7.  
  8.                        THE HYPER-SPACE NAVIGATOR'S GUIDE
  9.                                       by
  10.                               Terrance E. Hodgins
  11.  
  12.                   copyright (C) 1990 by Terrance E. Hodgins,
  13.                              All rights reserved.
  14.  
  15.  
  16.                            Semi-Intelligent Systems
  17.                                  PO BOX 4492
  18.                             ALBUQUERQUE, NM 87196
  19.  
  20.                             Compuserve:  76416,553
  21.                      Internet:  76416.553@compuserve.com
  22.                  Internet: terry%scopes.unm.edu@ariel.unm.edu
  23.  
  24.  
  25.         And now the boring legal stuff:
  26.  
  27.  
  28. This document uses the following trademarks:
  29.  
  30. AST is a registered trademark of AST Research, Inc.
  31.  
  32. IBM, PC-DOS, PC/XT, and PC/AT are registered trademarks of International Busi-
  33. ness Machines Corporation.
  34.  
  35. Intel is a registered trademark of Intel Corporation.
  36.  
  37. Lotus is a registered trademark of Lotus Development Corporation.
  38.  
  39. Microsoft, MS-DOS, Windows '286, and OS/2 are registered trademarks of  Micro-
  40. soft Corporation.
  41.  
  42. Semi-Intelligent  Systems,  The Hyper-Space Library,  Get-High,  HI-DOS,  High
  43. Code,  Xcode,  and  Mode Code are registered  trademarks  of  Semi-Intelligent
  44. Systems.
  45.  
  46. Unix is a registered trademark of AT&T, Inc.
  47.  
  48.  
  49.  
  50.  
  51.                             Disclaimer of Warranty
  52.  
  53.         TERRANCE E. HODGINS, AND SEMI-INTELLIGENT SYSTEMS, EXCLUDE ANY AND ALL
  54. IMPLIED WARRANTIES, INCLUDING WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  A
  55. PARTICULAR PURPOSE.
  56.  
  57.         NEITHER  TERRANCE E. HODGINS, NOR SEMI-INTELLIGENT SYSTEMS,  MAKE  ANY
  58. WARRANTY  OF REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO  THESE
  59. PROGRAMS,  THEIR QUALITY, PERFORMANCE, MERCHANTABILITY, OR FITNESS FOR A  PAR-
  60. TICULAR PURPOSE.
  61.  
  62.         NEITHER TERRANCE E. HODGINS, NOR SEMI-INTELLIGENT SYSTEMS, SHALL  HAVE
  63. ANY LIABILITY FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  64. OR RESULTING FROM THE USE OR MODIFICATION OF THESE PROGRAMS.
  65.  
  66.         THE USE OF THE 80286 LOADALL INSTRUCTION IS INHERENTLY DANGEROUS,  AND
  67. CAN RESULT IN PROGRAM CRASHES, OR RUN-AWAY PROGRAMS, WHICH CAN ALTER,  DAMAGE,
  68. OR  DESTROY COMPUTER DATA, AND WHICH CAN DAMAGE OR DESTROY COMPUTER  HARDWARE.
  69. USE ONLY AT YOUR OWN RISK.
  70.  
  71.  
  72.  
  73.  
  74.  
  75.                               Introduction
  76.  
  77.  
  78.  
  79.         Yes,  there really is an unpublicized, almost secret,  instruction  in
  80. the 80286, which has the ability to do several supposedly impossible things.
  81.  
  82.         It is called Loadall.
  83.  
  84.         What  Loadall does is completely load all the registers of  the  80286
  85. from  a table starting at 80:0 in low memory.  I do mean ALL registers:  every
  86. register  you ever heard of, and a few you haven't, and also  the  "invisible"
  87. internal registers which are NOT OTHERWISE programmable.  Executing a  Loadall
  88. nearly completely re-defines the CPU's state.
  89.  
  90.         This  means  that  it is a great warp,  or  hyper-space,  instruction:
  91. executing  a Loadall will jump you to someplace new, and leave you  with  your
  92. choice  of  register contents, status and mode settings,  and  memory  segment
  93. mappings,  allowing  you  to have your segments anywhere  in  the  16-megabyte
  94. address  space  of the 80286.  Those of you who are familiar with Unix  and  C
  95. programming  will be immediately reminded of the "longjump" routine.   Loadall
  96. is the ultimate long-jump.
  97.  
  98.         This  is possible in REAL mode.  You do NOT have to go into  protected
  99. mode  to get at memory above 1 Megabyte on the AT.  Which also means that  you
  100. don't have to then go through all kinds of odd-ball gyrations to get back  out
  101. of  protected mode.  And better yet, this instruction will work in  both  REAL
  102. and PROTECTED mode.
  103.  
  104.         Intel  included the Loadall instruction in the 80286 for chip  testing
  105. (they  can  throw the CPU into any state, and see if it then does what  it  is
  106. supposed  to  do),  but there are much better uses for it  than  that  (in  my
  107. not-so-humble opinion).
  108.  
  109.         The power of being able to re-program ANY and ALL of the registers  of
  110. the  CPU with one single instruction opens up a whole new world of  possibili-
  111. ties.
  112.  
  113.         Including, but not limited to:
  114.  
  115.         getting  at  all  the memory in your machine at will, even  if  it  is
  116. addressed above 1 megabyte, from real mode.
  117.  
  118.         executing real-mode programs in ram above one megabyte.
  119.  
  120.         installing a second operating-system-like program, or command  proces-
  121.  
  122. sor,  or shell, in memory above 1 megabyte, and alternating between  that  and
  123. DOS.
  124.  
  125.         installing most of the guts of custom TSR's, shells, and  device-driv-
  126. ers in ram above 1 megabyte (freeing up precious base memory), leaving in  low
  127. memory only the stubs to call the code upstairs.
  128.  
  129.         writing  very  large programs, which are "split", and  have  half  the
  130. program  residing  in  the low-down 640K, and the other half  up  in  extended
  131. memory, and running in either real or protected mode.
  132.  
  133.         installing  large  protected-mode programs in extended  memory,  where
  134. they  will not conflict with, or crowd out DOS, and ping-ponging between  them
  135. and DOS.
  136.  
  137.         switching to protected mode.
  138.  
  139.         emulating  real mode from protected mode (tough, and full of  gotchas,
  140. but still worth mentioning).
  141.  
  142.         this is really off-the-wall, but possible: building automata that  use
  143. Loadall  to  warp from state to state, sort of like a computer game  of  Life,
  144. played in the twilight zone.
  145.  
  146.         ? use your imagination. The sky's the limit.
  147.  
  148.         While the Loadall instruction only exists on the 80286 (to the best of
  149. anyone's  knowledge at present -- anyone who will talk, that is...),  the  386
  150. has other instructions which can accomplish much of the same functions.  Thus,
  151. it  is  possible  to write code that detects the  processor  being  used,  and
  152. switches  strategy accordingly, using subroutines with 386 op-codes to  accom-
  153. plish  the same functions on a 386.  Microsoft is already doing that in  their
  154. RamDrive.Sys and HiMem.Sys programs.  Thus you can have code which will run on
  155. both the 286 or 386, and makes the best use of each.
  156.  
  157.         This  instruction opens up so many possibilities (AND creates so  many
  158. problems)  for things like alternate operating systems, and alternate  shells,
  159. that live above 1 megabyte, in real mode or protected mode, that I foresee the
  160. need  for  a community library of "Hyper-Space" subroutines, which  can  still
  161. work properly even though some segments are in outer space, or the 80286 is in
  162. protected  mode.  I would be happy to collect these, and pass on the  best  of
  163. them with future distributions of this book and software.
  164.  
  165.         Please forgive all the legalistic warning messages.  If used properly,
  166. and  carefully, the Loadall instruction can be quite safe.  You  haven't  seen
  167. Microsoft  Ramdrive.Sys  destroying any systems lately, have you?   It's  just
  168. that  a  few bothersome people love to sue for anything, so you just  have  to
  169. plaster those stupid warning messages all over everything.
  170.  
  171.  
  172.  
  173.  
  174.                                    LOADALL
  175.  
  176.  
  177.         Okay, so what IS the Loadall instruction?
  178.  
  179.         Simple:
  180.  
  181.         *** 0F 05 hex ***
  182.  
  183.         So how does it work?  Well, I've already told you the gist of it:  all
  184. CPU  registers  are loaded from a 51-word table of data that starts  at  80:0h
  185. (absolute  24-bit  address 800h).  This address is one thing  that  cannot  be
  186. changed or re-programmed. It's hard-wired into the chip, and that's that.  And
  187. that's unfortunate, because all versions of anybody's DOS earlier than version
  188. 3.3 use that area for critical system code.
  189.  
  190.         Loadall takes no operands, and is just a two-byte instruction. All the
  191. "operands" for the instruction are obtained from the table at 80:0h.
  192.  
  193.         Just put "db 0Fh, 05" in your code stream, and watch the fun. But  you
  194. had better get that table right before you do, or else... (crash).
  195.  
  196.  
  197.                 ** THE LOAD TABLE **
  198.  
  199. -----------------------------------------------------------
  200. Address         Size            CPU register
  201.                 (words)
  202. -----------------------------------------------------------
  203.  
  204. 800             3       unused  (?? I don't believe it.)
  205. 806             1       MSW (Machine Status Word)
  206. 808             7       unused  (?? I don't believe it.)
  207. 816             1       TR (Task Register)
  208. 818             1       Flag Word
  209. 81A             1       IP (Instruction Pointer)
  210. 81C             1       LDT (Local Descriptor Table)
  211.  
  212. 81E             1       DS (Data Segment, or DS Selector)
  213. 820             1       SS (Stack Segment, or SS Selector)
  214. 822             1       CS (Code Segment, or CS Selector)
  215. 824             1       ES (Extra Segment, or CS Selector)
  216.  
  217. 826             1       DI (Destination Index)
  218. 828             1       SI (Source Index)
  219. 82A             1       BP (Base Pointer)
  220. 82C             1       SP (Stack Pointer)
  221.  
  222. 82E             1       BX (Data Register BX)
  223. 830             1       DX (Data Register BX)
  224. 832             1       CX (Data Register BX)
  225. 834             1       AX (Data Register BX)
  226.  
  227. 836             3       ES Descriptor Cache
  228. 83C             3       CS Descriptor Cache
  229. 842             3       SS Descriptor Cache
  230. 848             3       DS Descriptor Cache
  231.  
  232. 84E             3       GDTR
  233.                         (Global-Descriptor-Table Register)
  234.  
  235. 854             3       LDTDC
  236.                         (Local-Descriptor-Table Descriptor Cache)
  237.  
  238. 85A             3       IDTR
  239.                         (Interrupt-Descriptor-Table Register)
  240.  
  241. 860             3       TSSDC
  242.                         (Task-State-Segment Descriptor Cache)
  243.  
  244. total =         33h words == 102. bytes
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251.                       THE DESCRIPTOR CACHE ENTRIES
  252.  
  253.                       (DSDC, SSDC,CSDC, and ESDC)
  254.  
  255.  
  256.  
  257.         Wait a minute, forward-referencing again!  What's a descriptor? You've
  258. already used that word up above, and never defined it.
  259.  
  260.         Well  okay. A segment descriptor is a four-word structure of  informa-
  261. tion that describes a segment.  A descriptor gives a segment's size and 24-bit
  262. starting  address, and has a byte of encoded information, called  the  "access
  263. byte", that describes the characteristics of the segment (like whether it is a
  264. code segment or a data segment, writable or write-protected, and so on).   And
  265. the  desciptor  also has a dummy zero word for upward compatibility  with  the
  266. 80386.  Segment Descriptors are used in protected mode, but not in real mode.
  267.  
  268.         In  protected  mode,  when you want to use a segment  of  memory,  you
  269. reference  the  segment descriptor.  The 80286 looks into a table  or  two  of
  270. descriptors (which can be quite large, up to 16384 entries), to find the right
  271. entry, and find out what the segment is. If you had to do this every time  you
  272. referenced a memory variable, it would be terribly slow.  In order to  prevent
  273. this  overhead, saving the descriptor information for the current segments  in
  274. quickly-accessible CPU registers is a must.  That's what the descriptor caches
  275. are for.
  276.  
  277.         But you said they aren't used in real mode, right?  Right.  The  soft-
  278. ware  descriptor tables aren't.  But the hardware descriptor caches are.   The
  279. Intel  book on the 80286 seems mighty thin when it comes to telling  you  pre-
  280. cisely  what the protected-mode hardware does while in real mode, but some  of
  281. it  still works, and is very important (the descriptor caches in  particular).
  282. The  descriptor  caches determine where your segments really are,  whether  in
  283. real or protected mode.
  284.  
  285.         In  real  mode, your segments are all normally 64 Kbytes in  size,  by
  286. default,  and  are always located in the lowest megabyte of  the  80286's  16-
  287. megabyte  address space.  When you want to access a segment, you just  load  a
  288. number for the start of the segment into the appropriate segment register, and
  289. then read or write that segment of memory.
  290.  
  291.         The  segment number that you load is the address scaled down  by  four
  292. bits,  so that it really addresses a memory address that is sixteen times  the
  293. number  you  gave  it.  You can address anywhere inside  that  64  Kbyte-sized
  294. window by using an offset.
  295.  
  296.         Since the segment registers are 16 bits in size, and have been  scaled
  297. by four bits, you have the equivalent of 20-bit addressing, and can address  a
  298. 1-megabyte sized area.  That's real mode.
  299.  
  300.         Did it ever occur to you that that 1-megabyte sized area might  itself
  301. just be appearing somewhere inside of an even larger area?
  302.  
  303.         And that the 1-megabyte-sized real-mode area is made to start at  zero
  304. when the 80286 chip is reset, but doesn't have to stay there forever?
  305.  
  306.         I  mean, if in protected mode, the hardware is there to address a  16-
  307. megabyte-sized  address space, well, that hardware doesn't just go  away  when
  308. you are in real mode, does it?  Or all just get turned off?
  309.  
  310.         No,  it doesn't.  As a matter of fact, it still works just  fine,  but
  311. you  weren't given any instructions for doing anything with that part  of  the
  312. hardware from real mode.  Or were you?
  313.  
  314.         Oh yes you were.  It's called LOADALL.
  315.  
  316.         So  how  do the hardware desciptor caches work?  Well, they  hold  the
  317. information  that was read from a (software) descriptor in memory.  The  80286
  318. discards  the unused zero word, and keeps the rest.  When you address  memory,
  319. you  are actually using the segment addresses in the descriptor  cache  regis-
  320. ters, not what is in the segment registers.
  321.  
  322.         Perhaps you thought you were using the segment registers for  address-
  323. ing:  it sure looks like you do, because if you load something into a  segment
  324. register, you will then address the memory that the segment register is point-
  325. ing to.  What is happening invisibly in the background is that the correspond-
  326. ing  descriptor cache is being updated whenever you load a  segment  register,
  327. and then the descriptor cache is being used for the actual addressing.
  328.  
  329.         So  the segment descriptor caches, and not the segment registers,  are
  330. what  actually  control what goes out on the address lines,  and  hence,  what
  331. memory  you will really address. And the addresses in the  segment  descriptor
  332. caches are 24-bit addresses.  Now isn't that special?
  333.  
  334.         So if we can use Loadall to load anything we want to into the  segment
  335. descriptor  caches,  then we can address anywhere in the  16-megabyte  address
  336. space of the 80286, right?  Right.  You got it.
  337.  
  338.  
  339.         The contents of the descriptor cache entries in a Loadall table are:
  340.  
  341.          The  absolute 24-bit address for the start of the  segment,
  342.                  in  the usual Intel  lowest-byte-first  byte-order.
  343.                  That is, the bytes are: lowest, middle, highest.
  344.  
  345.  
  346.          An  access byte, customarily set to 92h or 93h.  This  byte
  347.                  is  encoded in the usual way that access bytes  are
  348.                  encoded in Global Descriptor Table entries (see the
  349.                  accompanying  charts).   This  byte  describes  the
  350.                  characteristics of the segment, like whether it  is
  351.                  code or data, and write-protected or not.
  352.  
  353.          A  16-bit segment limit.  This is the segment  size,  minus
  354.                  one.  FFFFh is equal to a full 64K.
  355.  
  356.  
  357.         This  ordering is exactly backwards, word-order-wise, from  the  usual
  358. layout  of the descriptors used in the protected-mode tables like  the  Global
  359. Descriptor Table.  This is because the Loadall instruction is essentially just
  360. a  giant  POP-ALL instruction.  The word order is backwards  (really,  "Stack-
  361. wards"), but the byte order within words is not reversed.
  362.  
  363.         The addresses loaded into the descriptor caches must be 24-bit  "abso-
  364. lute"  or un-segmented "flat-space" versions of the segment  start  addresses.
  365. IE: a segment address of 3456h becomes an absolute address of 034560h.  Remem-
  366. ber  that segment addresses are ordinarily scaled down by 4 bits.  So we  have
  367. to scale them back up to get the 24-bit flatland equivalent.
  368.  
  369.         You will notice that there seems to be some duplication of information
  370. here:  you have a CS register slot in the Loadall table, which is loaded  with
  371. the  desired  code  segment start address, and you also have  a  Code  Segment
  372. Descriptor  Cache  entry, with an address slot which is loaded with  much  the
  373. same information.  The same is also true of DS, SS, and ES.
  374.  
  375.         They can't always be the same, because one is 16 bits, and one 24, and
  376. the 24-bit descriptor cache entry can specify the address down to the byte, in
  377. the full 16-megabyte address space, while the 16-bit segment register can only
  378. address on 16-byte boundaries, and can't address beyond 1 megabyte.
  379.  
  380.         So  if  they are different, which ones win out?   The answer  is,  the
  381. Descriptor Caches.  They have to, because only the Descriptor Caches have  the
  382. whole  24-bit address necessary for addressing the entire 16-megabyte  address
  383. space of the 80286.  Also because the Descriptor Caches are what are  actually
  384. wired  to the address lines.  In protected mode, the segment  registers  don't
  385. even get close to the address lines.
  386.  
  387.         But  watch  out: this gets tricky.  For a  simple  rule-of-thumb,  the
  388.  
  389. proper  programming practice to follow is: in real mode, always keep them  the
  390. same.  That is, where the bits of the two overlap, keep them the same.  The CS
  391. register  really holds the equivalent to bits A3 to A19 of the 24-bit  address
  392. in the CS Descriptor Cache, so there is no way that you can "keep them all the
  393. same".  But you can keep those bits the same, and you will want to.
  394.  
  395.         Why?  Because, in real mode, certain operations will update a Descrip-
  396. tor Cache using the contents of the paired Segment Register.  Oh yeh?  Yeh.
  397.  
  398.         Example:
  399.  
  400.         Even  if the code segment entry "CS" in a Loadall table  is  blatantly
  401. wrong,  but the value in the Code Segment Descriptor Cache "CSDC" is  correct,
  402. and  the Instruction Pointer "IP" value is correct, and you do a Loadall,  the
  403. Loadall will still work, and you WILL run the code that you intended to be run
  404. after  the Loadall, but the program will crash at the first  jump  instruction
  405. after  the  Loadall.  Calls to subroutines will likewise crash if  the  CS  is
  406. wrong.
  407.  
  408.         The  jump  or call instruction causes updating of  the  CS  Descriptor
  409. Cache  contents, using the contents of the CS register, and the offset in  the
  410. jump  or  call instruction.  So your CS descriptor cache goes  from  right  to
  411. wrong,  without any further help from you.  That's why you have to "keep  them
  412. the same".
  413.  
  414.         (This  is just a simple rule of thumb.  Like all simple  rules,  there
  415. are exceptions, and the rules can be broken.  Breaking these rules doesn't buy
  416. you  anything, but you might note that this is simply a rule of thumb,  not  a
  417. Commandment From On High.)
  418.  
  419.         The same is also true of the other Segment Registers, and their match-
  420. ing Descriptor Caches, although the instructions that will cause updating will
  421. differ.   The  commonest operation that causes updating  of  these  descriptor
  422. caches is loading a new segment value into a segment register.
  423.  
  424.         Now obviously, not all the bits of the address in the Descriptor Cache
  425. will be updated by such operations.  The highest 4 bits cannot be updated from
  426. the  segment register, because there are no corresponding bits.  So what  does
  427. it do with them?  In real mode, the worst.  It clears them.  Try doing a  jump
  428. while  executing real-mode code upstairs, above 1 Megabyte, and you will  come
  429. crashing down out of the sky.  A simple jump in code located way upstairs will
  430. turn  into  a very long jump to the lowest megabyte of memory.   Probably  not
  431. what you had in mind, at all.  Far jumps and far calls are out of the question
  432. for the same reason.
  433.  
  434.         Curiously,  a  call will not cause you to fall out of the sky  in  the
  435. same  way as a jump will, so we can do reversed jumps, or reversed  calls,  by
  436. shoving  a return address, and then a destination address onto the stack,  and
  437. then executing a return instruction, where a jump to precisely the same  place
  438. will crash us.
  439.  
  440.  
  441.         When  you are executing code in real mode, above 1 megabyte  (plus  64
  442. K), your position is as precarious as that of Icarus flying towards the sun on
  443. wings  held  together with wax.  (More on that "plus 64 K" note  later.)   You
  444. must  keep  interrupts turned off because ANY interrupt will  yank  you  down-
  445. stairs,  and you won't return upstairs again.  The interrupt  service  routine
  446. will  change some segments or other, particulary the Code Segment,  and  those
  447. segments'  descriptor  caches will have the highest  four  bits  irretrievably
  448. cleared.
  449.  
  450.         And  the  updating  of the  lowest four bits is an  open  question.  I
  451. always  set  my segments on 16-byte boundaries so I don't  get  burned  there.
  452. That  is, the four lowest bits of the 24-bit address are always  zero.   Thus,
  453. the  16-bit  segment settings in the segment registers will always  match  the
  454. values of the lowest 20 bits of the descriptor cache settings.
  455.  
  456.         Here's what these descriptor cache entries look like, in source  code,
  457. with a set of default values plugged in:
  458.  
  459. newESDC         dw      0,      9200h,  0FFFFh
  460. newCSDC         dw      0,      9200h,  0FFFFh
  461. newSSDC         dw      0,      9200h,  0FFFFh
  462. newDSDC         dw      0,      9200h,  0FFFFh
  463.  
  464.         The running program will replace those zeroes in the first and  second
  465. words of each entry with real addresses before doing the Loadall.
  466.  
  467.         The "92"'s are the access bytes, and mean: "this item is a  descriptor
  468. of  a data segment, it is valid, it has the highest possible  privilege  level
  469. (0), writing to it is okay, and it has not been accessed" (really, written to.
  470. A 'dirty' page, in virtual-memory-system parlance).
  471.  
  472.         Those  "FFFF"'s set up segments 64K in size. There's no point in  set-
  473. ting  them  any smaller, and a lot of grief to be gotten if you  do.  So  just
  474. always set them to "FFFF" in real mode.
  475.  
  476.  
  477.  
  478.  
  479.  
  480.                        THOSE OTHER BIG REGISTERS
  481.  
  482.  
  483.         GDTR    Global Descriptor Table Register
  484.         LDTDC   Local-Descriptor-Table Descriptor Cache
  485.         IDTR    Interrupt Descriptor Table Register
  486.         TSSDC   Task-State-Segment Descriptor Cache
  487.  
  488.         These  registers do next to nothing while in real mode.  The  strategy
  489. for dealing with these is: just set them up in an acceptable manner, and  then
  490. forget them.  The Interrupt Descriptor Table Register is the most important of
  491. these,  as  it  really does determine the starting address  of  the  interrupt
  492. vector table.
  493.  
  494.         The format of the data for these registers is just about identical  to
  495. the  format  of the data in the Descriptor Caches, except for an  unused  byte
  496. (there is no access byte):
  497.  
  498.         an absolute 24-bit address for table start, in the
  499.                 usual Intel byte-order. That is, the bytes
  500.                 are: lowest, middle, highest.
  501.  
  502.         an "extra", or "trash", or "dummy" byte (pick your
  503.                 favorite name.)  Set to either FFh or 0.
  504.  
  505.         a 16-bit limit.  This is the table size, minus one.
  506.                 (FFFFh == a full 64K)
  507.  
  508.  
  509.  
  510.        Set up the GDTR (Global Descriptor Table Register) and the IDTR (Inter-
  511. rupt  Descriptor Table Register) using the instructions "sgdt" and  "sidt"  --
  512. "store  global  descriptor table register", and  "store  interrupt  descriptor
  513. table register". These two instructions work in both real and protected mode.
  514.  
  515.        The  values that we get from them are somewhat goofy (especially  since
  516. we  are getting data about non-existent tables), but we use those values  any-
  517. way, just to keep the 80286 chip happy.  We will just stuff back into the chip
  518. whatever is already in there.
  519.  
  520.        The  LDTDC (Local-Descriptor-Table Descriptor Cache) is a real  nothing
  521. in real mode.  In real mode, there is no Local-Descriptor-Table Descriptor  to
  522. cache.  We just set the LDTDC with an acceptable size, same as the GDTR (88h),
  523. and let it go at that.
  524.  
  525.        The  TSSDC  (Task-State-Segment Descriptor Cache) is  likewise  a  null
  526.  
  527. register in real mode.  There is no Task State Segment to point to. Again,  we
  528. just  set  it up with a size that will keep the 80286 chip from  freaking  out
  529. (thinking that the segment is impossibly small), and let it go at that.
  530.  
  531.  
  532.  
  533.        Set up, just before doing a Loadall, these items will look like:
  534.  
  535. newGDTR         dw      D8A0h,  0FF00h, 88h
  536. newLDTDC        dw      0,      0FF0Eh, 88h
  537. newIDTR         dw      0,      0FF00h, 0FFFFh
  538. newTSSDC        dw      4000h,  0FF0Eh, 800h
  539.  
  540.  
  541.         The addresses in the newLDTDC and the newTSSDC are E0000h and  E4000h,
  542. respectively.   There is nothing at those addresses but stupid phantom  copies
  543. of the BIOS Roms, wasting precious low-memory address space.  So what I do  is
  544. put  the  non-existent tables on top of the non-existant ROMS,  and  let  them
  545. fight  it out.  In truth, those addresses in those descriptor caches'  entries
  546. will  never  really be used for anything, anyway, so they could  be  anywhere.
  547. They just don't matter.  The starting address of the IDTR is the only one that
  548. does matter.
  549.  
  550.  
  551.  
  552.                   AND ALL THOSE OTHER LITTLE REGISTERS
  553.  
  554.  
  555.         The  MSW  (Machine Status Word) is normally set to zero. On  the  286,
  556. only  the  4 lowest bits are even used.  The one super-important bit  in  this
  557. register  is  the mode bit.  Set it, and you warp into  protected  mode.   The
  558. other three bits are invalid and irrelevant if you are not in protected  mode.
  559. Zero this word, unless you really intend to go into unreal mode.  Heaven  help
  560. your program if you set it, and have not set up all the descriptor tables, and
  561. all the protected-mode registers, and cross-linked all the pointers to  every-
  562. thing, correctly, first.  We will get into that can of worms later.
  563.  
  564.         Just for reference, here's what the bits are:
  565.  
  566.         D0      == PE Protected-Mode Enable  (yeh, this is IT.)
  567.         D1      == MP Monitor Process
  568.         D2      == EM Emulate Processor Extension
  569.         D3      == TS Task Switched
  570.  
  571.  
  572.         The  TR  (Task Register) is another register used  only  in  protected
  573. mode. It is used for keeping track of which task is running.  Not our  problem
  574. in real mode.  Zero it.
  575.  
  576.         The  Flag Word is the same old flag word that we are already  familiar
  577. with  from ordinary real-mode programming.  We just push the flags word  here,
  578. and  we've done it.  Or we can zero it.  None of our programs are going to  do
  579. anything as off-the-wall as a conditional jump right after a Loadall,  anyway,
  580. right?  Uh, right?  Why do I see you grinning?  12-dimensional Life, huh?
  581.  
  582.         The IP (Instruction Pointer) is critical.  This one really works.  The
  583. address we put here will, in combination with the address in the Code  Segment
  584. Descriptor Cache (CSDC), determine where we will start executing code  immedi-
  585. ately after the Loadall.  So this acts like a jump vector.  We set this up  in
  586. the  our programs, just before doing a Loadall, do determine where we will  go
  587. next.  Better get this one right.
  588.  
  589.         The  LDT  (Local Descriptor Table) is another null  register  in  real
  590. mode.  Zero it.
  591.  
  592.  
  593.         DS (Data Segment, or DS Selector)
  594.         SS (Stack Segment, or SS Selector)
  595.         CS (Code Segment, or CS Selector)
  596.         ES (Extra Segment, or CS Selector)
  597.         Set these up so that they contain the same number as bits A4 to A19 of
  598. the  corresponding Segment Descriptor Cache.  These work in  conjunction  with
  599. those.
  600.  
  601.  
  602.  
  603.         All  of the following registers are very straight-forward:  just  load
  604. them  with whatever you want the registers to have after the Loadall.  If  you
  605. are  not trying to carry values in these registers, you can just default  most
  606. all of them to zeroes.
  607.  
  608.         The stack pointer requires some care, as the stack is one of the  best
  609. ways to carry data into the beyond.  I generally stuff the stack just before a
  610. Loadall,  and then write the current stack pointer to the SP slot in the  Loa-
  611. dall table, so that I know that I have it right.
  612.  
  613.         DI (Destination Index)
  614.         SI (Source Index)
  615.         BP (Base Pointer)
  616.         SP (Stack Pointer)
  617.  
  618.         BX (Data Register BX)
  619.         DX (Data Register DX)
  620.         CX (Data Register CX)
  621.         AX (Accumulator AX)
  622.  
  623.  
  624.         And then these little curiosities: the two "dead" spots in the table.
  625.  
  626. 800     3 words         unused   (?? I don't believe it.)
  627. 808     7 words         unused   (?? I don't believe it.)
  628.  
  629.         Obviously, they are there for something.  They must load some  invisi-
  630. ble register or other.  The registers might be some very transient  registers,
  631. just for intermediate products, which may not be useful...
  632.  
  633.         Then  again,  considering how much we haven't been told so  far,  they
  634. might be good for something.  This is another area for future experimentation.
  635. In the mean time, zero them.
  636.  
  637.  
  638.                   AND A PRETTY-TOGETHER DEFAULT TABLE
  639.  
  640.  
  641.         So  here's  what  a  default  Loadall  table  looks  like.  Note  that
  642. "new_Reg_Buf" doesn't label any data item that we really use; it's the name of
  643. the whole table.
  644.  
  645. ;    LOADALL Register Load Table for new values to be loaded
  646. ;    into registers by a Loadall.
  647.  
  648. new_Reg_Buf     dw      3 dup (0)       ; unused space
  649. newMSW          dw      0
  650. newDead         dw      7 dup (0)       ; unused space
  651. newTR           dw      0
  652. newFlagWord     dw      0
  653. newIP           dw      offset after_ldall      ; * may chng
  654. newLDT          dw      0
  655.  
  656. newDS           dw      0       ; *chng
  657. newSS           dw      0       ; *chng
  658. newCS           dw      0       ; *chng
  659. newES           dw      0       ; *chng
  660.  
  661. newDI           dw      0
  662. newSI           dw      0
  663. newBP           dw      0
  664. newSP           dw      0       ; *chng
  665.  
  666. newBX           dw      0
  667. newDX           dw      0
  668. newCX           dw      0
  669. newAX           dw      0
  670.  
  671. newESDC         dw      0,      9300h,  0FFFFh  ; *chng
  672. newCSDC         dw      0,      9300h,  0FFFFh  ; *chng
  673. newSSDC         dw      0,      9300h,  0FFFFh  ; *chng
  674. newDSDC         dw      0,      9300h,  0FFFFh  ; *chng
  675.  
  676. newGDTR         dw      D8A0h,  0FF00h, 88h     ; @ 0D8A:0 *n
  677. newLDTDC        dw      0,      0FF0Eh, 88h     ; @ E000:0
  678. newIDTR         dw      0,      0FF00h, 0FFFFh  ; @ 0000:0 *n
  679. newTSSDC        dw      4000h,  0FF0Eh, 800h    ; @ E400:0
  680.  
  681.  
  682.         Those  "*chng" comments mean that those items MUST be changed  by  the
  683. running program before actually doing the Loadall. We cannot correctly default
  684. them in the sources because the correct values can only be determined at  run-
  685. time.
  686.  
  687.  
  688.  
  689.         The "*n" means that those values are not really in the default  tables
  690. in the sources: the running program uses the sgdt and sidt instructions to get
  691. those values and then plugs them into those two entries.  Just letting you see
  692. what they will look like. You could have anything in the original table there,
  693. because  the running program will over-write those items with  correct  values
  694. anyway.
  695.  
  696.         The "@ 0D8A:0" comments are just noting the addresses in those  items,
  697. in a more readable form.
  698.  
  699.  
  700.  
  701.  
  702.                         GATE A20 : Door to the Beyond
  703.  
  704.  
  705.         Before  we get heavy into the guts of actually using the  Loadall  in-
  706. struction, we need to touch on this item: Gate A20.  Loadall is almost useless
  707. without control of Gate A20.
  708.  
  709.         Gate  A20  is the gate on the motherboard of the AT  that  enables  or
  710. disables  the  4  highest  address  lines, A20 to A23.  In  order  to  be  PC-
  711. compatible, they are ordinarily disabled on an AT.  The pathetic PC could only
  712. address 1 megabyte of space, total, remember?  That's 20 bits.  If those lines
  713. are disabled, then addressing wraps to zero above FFFF:0010.  But if they  are
  714. enabled,  then addressing doesn't wrap, and you can address above 1  Megabyte.
  715. This  has nothing to do with protected mode.  Even if the 80286 were  in  pro-
  716. tected mode, it still couldn't address above 1 Megabyte without enabling  Gate
  717. A20.
  718.  
  719.         In  the part of the Hyper-Space Library freely distributed  with  this
  720. document  and the View-XM program are routines called "A20_on" and  "A20_off".
  721. They  need no arguments.  You just call them, and they will enable or  disable
  722. Gate A20.  Do not make a habit of turning Gate A20 on and just leaving it  on,
  723. as  rumor has it that some barbaric programmers from the bad-old days  made  a
  724. habit  of depending on address-wrapping, addressing something like  FFFF:0345h
  725. to  get at 0:0335h.  Ugh!  These subroutines also check whether Gate  A20  was
  726. already on before the call, and if so, leave it alone.
  727.  
  728.         This  leads us to a very interesting twist in the game:  what  if  you
  729. turn  on Gate A20, and load FFFFh into a segment register, like the DS  regis-
  730. ter,  and then address something like DS:0300h?  The answer is, you  will  ad-
  731. dress  beyond 1 megabyte, without either going into protected mode,  or  using
  732. Loadall  tricks.  The  PC can only address 1 Megabyte total, but  the  AT  can
  733. address 1 Megabyte, plus 64K, minus 16 bytes, in REAL mode, without Loadall.
  734.  
  735.         This a big part of the XMS driver specification.  That's the  eXtended
  736. Memory Specification (not to be confused with the "EMS" Expanded Memory Speci-
  737. fication).  The XMS driver accesses memory addressed above 1 Megabyte on AT's.
  738.  
  739.         You can write programs which use standardized calls to the XMS driver,
  740. and  expect that the program will work with anyone's XMS  driver.   Microsoft,
  741. Intel, Lotus, and AST Research (the authors) have put the XMS specification in
  742. the  public domain (although they retain the copyright), and it  is  currently
  743. supported  by them, and probably by many more companies that I don't know  of,
  744. so we should be seeing plenty of good XMS device-drivers around, and, in turn,
  745. programs using it.
  746.  
  747.         Furthermore,  Microsoft  will give you a copy of the XMS  driver,  and
  748. standard,  free,  if you write to them and ask for one.   Write  to  Microsoft
  749.  
  750. Corporation,  16011  NE 36th Way, Box 97017, Redmond WA  98073,  and  politely
  751. request  a  floppy copy of the XMS standard and driver.  The  same  files  are
  752. available from many bulletin board systems, and anonymous FTP sites.
  753.  
  754.         Since  you  want a nice, clean, non-colliding standard  way  for  your
  755. programs to be able to get at more ram, using the EMS and XMS standards is the
  756. only  good  way to go.  Throughout this book, we are going  to  support  those
  757. standards, and others, too.
  758.  
  759.         The  recommended  programming practice is to always  support  the  XMS
  760. standard, and use requests to the XMS driver to get at extended memory, rather
  761. than just brute-force doing it yourself, even though you can with Loadall,  so
  762. that your programs will not conflict with others.
  763.  
  764.         The PC world is already far too filled with gotchas and incompatibili-
  765. ties,  and things that collide with other things, for us to be adding  to  the
  766. misery.
  767.  
  768.         The one thing that the XMS driver adds, that you will not have if  you
  769. just  take  over and use an area of extended memory yourself, is any  kind  of
  770. collision  prevention  or co-ordination between programs.  You won't  know  if
  771. another  program is already using that area, but the XMS driver will, as  long
  772. as the other program is also using the driver.  So everybody better be  adher-
  773. ing to the standard!
  774.  
  775.         On  the  other  hand, you would not be reading  this  book  about  the
  776. "secret"  Loadall  instruction if you were all that committed  to  ONLY  using
  777. "normal" standards, would you?  The trick is to support the standards, without
  778. being  constrained  by them.  This requires great care and thought  about  the
  779. consequences  of any use of Loadall for "non-standard" activities.   You  can,
  780. for  instance, allocate some memory, using the XMS driver, and then  go  ahead
  781. and use Loadall to do anything you want to with it, since you now own it.  You
  782. have the best of both worlds.
  783.  
  784.         And so what do the XMS drivers use to get at the extended memory above
  785. the High-Memory Area?  Either going into protected mode, or Loadall.
  786.  
  787.  
  788.  
  789.  
  790.                        THE PROCEDURE FOR USING LOADALL
  791.                        (the ultra-safe, long procedure)
  792.  
  793.  
  794. 1.      Save  the  original machine state, so you have a state to  return  to.
  795. This information can be saved in a Loadall table, which is the most convenient
  796. form for later use.
  797.  
  798. 2.      Disable interrupts.  Just in case.  We want a clean copy of area 80.
  799.  
  800. 3.      Save  the 102-byte (33h words) block of data located at  80:0h.   Ver-
  801. sions  of DOS (both PC- and MS-) earlier than 3.3 use this area  for  critical
  802. system code, and as of DOS 3.3, RamDrive.Sys, and Himem.Sys use this area  for
  803. their own Loadall tables.
  804.  
  805. 4.      Re-enable  interrupts.   Let the clock ticks,  or  whatever,  through,
  806. while we do the following step.
  807.  
  808. 5.      Set  up  the new Loadall table (new_reg_buf), which  defines  the  new
  809. state we want to warp to.
  810.  
  811. 6.      Disable Interrupts.
  812.  
  813. 7.      Copy the new Loadall table to 80:0h.
  814.  
  815. 8.      Execute a Loadall.
  816.  
  817. 9.      Do  something  or other with your new machine state.   Read  or  write
  818. extended memory, run code upstairs, or whatever.
  819.  
  820. 10.     Copy the "old" Loadall table, containing the saved machine state, down
  821. to 80:0.
  822.  
  823. 11.     Do  another Loadall (Un-Loadall.)  This restores the original  machine
  824. state.
  825.  
  826. 12.     Copy the block of saved data back to 80:0h.
  827.  
  828. 13.     Re-enable interrupts.
  829.  
  830.         And you have done it.
  831.  
  832.  
  833.         This is the long, drawn-out method.  There are various short-cuts  and
  834. speedups possible.
  835.  
  836.         If all you have been doing is reading or writing extended memory,  for
  837. instance,  then  you  don't have to do the second loadall.   Just  changing  a
  838. segment register (loading a new value) will cause the corresponding Descriptor
  839. Cache  to drop its four highest address bits, restoring addressing to the  low
  840. megabyte.
  841.  
  842.         Read the sources for the program "View-XM" for more details on this.
  843.  
  844.         See the full text of The Hyper-Space Navigator's Guide for more.
  845.  
  846.  
  847.  
  848.  
  849.         The  Hyper-Space Navigator's Guide, the book and software library,  is
  850. available   from  Semi-Intelligent  Systems  for  $49.00  (students  get   20%
  851. discount),  and comes with the floppy of source code.  With other  books,  you
  852. have  to pay $10 or $20 more to get the floppy that should have come with  the
  853. book  in  the  first place.  Here you don't.  It is available  on  any  common
  854. floppy  format:  5.25" 360K or 1.2MB, or 3.5" 720K.  If you order  it,  please
  855. state your floppy format preference.
  856.  
  857.         FULL source code in assembly and C is provided.
  858.  
  859.         The Hyper-Space Navigator's Guide gives the full low-down on  Loadall,
  860. and other 286- and 386-compatable extended-memory tricks, too:  the good,  the
  861. bad, and the ugly.
  862.  
  863.         The  book comes with a library of subroutines designed  to  facilitate
  864. the use of extended memory, and includes numberous demo programs which do just
  865. about everything you can do with Loadall (or without), including:
  866.  
  867.         reading and writing extended memory.
  868.  
  869.         running code up there, in both real and protected mode.  Yes, you  can
  870. use  Loadall  to  warp directly into protected mode.  Or you  can  do  it  the
  871. "normal"  way, so that the code will be 386-compatable.  Both ways are  imple-
  872. mented in the code.
  873.  
  874.         going  into,  and running in, and then getting back out  of  protected
  875. mode,  from within your own programs, on both the 286 and 386.   Getting  into
  876. protected mode is relatively easy.  Try getting back out on a 286.  I'll  show
  877. you how.
  878.  
  879.         writing  "split"  programs,  with a low-memory half, and  a  high-  or
  880. extended-memory  half,  with the second half in real or protected  mode.   The
  881. cat's meow for image-processing programs which eat memory space like popcorn.
  882.  
  883.         installing  either real- or protected-mode "high code" inside  an  ex-
  884. tended-memory  ram-disk file, where it won't collide with anything or  anybody
  885. else,  and then using a TSR to launch directly into running the code  from  in
  886. there (thus turning a piece of the ram-disk back into ram).
  887.  
  888.         Again,  full and complete source code, so that the demo programs  also
  889. supply  you  with hackable skeletons for quickly building your  own  programs,
  890. (without  the  many months of day-and-night hacking and  hair-tearing  I  went
  891. through  to  figure out this stuff).  Just throw away the middle of  the  demo
  892. main routine and plug your code in.
  893.  
  894.