home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_GEN / XLIB40.ZIP / DONTREAD.ME next >
Text File  |  1994-03-25  |  52KB  |  885 lines

  1. ;                  A Tutorial for XLIB and Protected Mode
  2.  
  3.  
  4. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  5. ;   If you don't read much, then this file was made just for you.  It's 
  6. ;a lot shorter than XLIB.DOC.  It also explains several protected-mode 
  7. ;concepts, including some that are needed to understand XLIB.DOC.  We work
  8. ;through an assembly language program which demonstrates the usage of XLIB.
  9. ;As we go, we are going to do a lot of talking about protected mode in 
  10. ;general as well as protected mode under XLIB in particular.
  11. ;   First, let's talk about what XLIB is generally designed to do:  XLIB 
  12. ;will allow you to write protected-mode procedures using your familiar 
  13. ;language development tools (compilers, linkers, etc), and will allow you to
  14. ;execute code containing such procedures using DOS.  XLIB is also designed
  15. ;to make all this as simple for you as it possibly can.  That turns out to
  16. ;very simple indeed.
  17. ;   XLIB has one major shortcoming:  It relies on DOS to load executables
  18. ;and files.  This means that our code must reside in the first meg because
  19. ;that's the only thing DOS knows how to work with.  It also means that when
  20. ;we transfer disk files to or from extended memory, we must perform a 
  21. ;transfer through a buffer in the first meg.  In almost every other regard, 
  22. ;we will have unlimited power.  Most importantly, we will be able to access 
  23. ;data in extended memory with extreme ease and speed.
  24. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  25.  
  26. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  27. ;   Now, let's start with a little program.  We must begin with a model 
  28. ;declaration.  XLIB is a large-model library which uses PASCAL conventions.  
  29. ;We will use the same model here; however, this is not a requirement of XLIB.
  30. ;We also want to tell the assembler to let us use 32-bit instructions.
  31. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  32.  
  33.                .MODEL         LARGE,PASCAL
  34.                .386P
  35.  
  36. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  37. ;   The next two line are the key ingredients.  We want to provide XLIB 
  38. ;symbols to this program with XLIB.INC.  We also want to link with XLIBE.LIB.  
  39. ;XLIBE.LIB has exception trapping capabilities whereas XLIB.LIB does not.
  40. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  41.  
  42.                INCLUDE        XLIB.INC
  43.                INCLUDELIB     XLIBE.LIB
  44.  
  45. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  46. ;TASM Only:
  47.  
  48. ;   If you are a TASM programmer, then don't use the last two lines.  Use the
  49. ;following lines instead.  These lines include some useful macros that should 
  50. ;have been in TASM to begin with.
  51. ;
  52. ;              MASM51                        ;Emulate MASM51
  53. ;              QUIRKS                        ;MASM51 quirks are sometimes nice
  54. ;
  55. ;PUSHW         MACRO IMMEDIATE16:REST        ;PUSH 16-bit constant
  56. ;              IF (@WordSize EQ 4)
  57. ;              DB             66H
  58. ;              ENDIF
  59. ;              DB             68H
  60. ;              DW             IMMEDIATE16
  61. ;              ENDM
  62. ;
  63. ;PUSHD         MACRO IMMEDIATE32:REST        ;PUSH 32-bit constant
  64. ;              IF (@WordSize EQ 2)
  65. ;              DB             66H
  66. ;              ENDIF
  67. ;              DB             68H
  68. ;              DD             IMMEDIATE32
  69. ;              ENDM
  70. ;
  71. ;              INCLUDE        XLIBB.INC
  72. ;              INCLUDELIB     XLIBEB.LIB
  73. ;
  74. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  75.  
  76. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  77. ;  Now we will finish our simplified segment directives.
  78. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  79.  
  80.                .STACK         1024
  81.                .DATA
  82.                .CODE
  83.                .STARTUP
  84.  
  85. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  86. ;   Now we have to deal with an annoying complication.  XLIBE.LIB might need 
  87. ;to allocate some conventional memory for its own use.  The problem is that 
  88. ;our program has likely claimed all available memory, even though it isn't 
  89. ;going to use it.  If you are a MASM programmer, then the solution is simple:  
  90. ;Link with the CPARM:1 parameter.  If you are a TASM programmer, then you 
  91. ;must resize the memory block in which this program is contained.  The 
  92. ;following code will do the trick:
  93. ;
  94. ;              MOV            AX,SP          ;SS:SP = end of program
  95. ;              SHR            AX,4
  96. ;              MOV            BX,SS
  97. ;              ADD            BX,AX
  98. ;              INC            BX             ;BX = first para. beyond program
  99. ;              MOV            AX,ES          ;ES:0000 = first para. of program
  100. ;              SUB            BX,AX          ;BX = program size in para.
  101. ;              MOV            AX,4AH         ;Function to resize memory block
  102. ;              INT            21H            ;Carry will be set if failure
  103. ;              JNC            STILLGOING
  104. ;              MOV            AX,4C01H       ;Failed to resize so terminate
  105. ;              INT            21H
  106. ;STILLGOING:
  107. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  108.  
  109. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  110. ;   One more step and we will be ready to hit protected mode!!!  We must
  111. ;initialize the library by calling INITXLIB.  This procedure will return an
  112. ;error code in EAX.  A code of zero always means success under XLIBE.
  113. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  114.  
  115.                CALL           INITXLIB
  116.                OR             EAX,EAX
  117.                JZ             GOTPOWER
  118.                MOV            AX,4C01H       ;DOS termination function
  119.                INT            21H            ;You will have to read after all.
  120.                                              ;If you simply can't stand user
  121.                                              ;manuals, then try throwing out
  122.                                              ;some device drivers and TSRs.
  123. GOTPOWER:
  124.  
  125. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  126. ;   We don't want to mess around in real mode anymore.  As you will shortly 
  127. ;see, we can do about anything in protected mode that we could do in real 
  128. ;mode, plus a whole lot more.  Moreover, XLIBE makes protected mode easier 
  129. ;than real mode.  We will spend the rest of our time working from a 32-bit 
  130. ;protected-mode subroutine called PMMAIN.  We will get there with an XLIBE 
  131. ;procedure called CALLPM.  Just push the offset of PMMAIN on the stack and 
  132. ;call CALLPM.
  133. ;   There is one big assumption involved here:  It is assumed that PMMAIN and 
  134. ;all other 32-bit routines are contained in a segment called TSEG.  Don't 
  135. ;worry, TSEG can be larger than 64K if you live by the rules.  Read the 
  136. ;manual if you want to know what they are.
  137. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  138.  
  139.                PUSHD          OFFSET PMMAIN  ;Must use a 32-bit offset
  140.                CALL           CALLPM
  141.  
  142. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  143. ;   XLIBE is going to use the return address from the above call to find its
  144. ;way back to real mode.  You can get back to real mode by using the RET
  145. ;instruction provided that you have maintained the stack pointer.  If this is
  146. ;not the case, then you can still get back with no problem.  More about this
  147. ;later.  When we get back, we are just going to terminate.
  148. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  149.  
  150.                MOV            AX,4C00H
  151.                INT            21H
  152.  
  153. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  154. ;   Now we will declare our 32-bit segment.  We will later talk about
  155. ;protected mode segments in general and about 32-bit segments in particular.
  156. ;Just hang loose for now.  It is all simple.
  157. ;   When we arrive at this segment from CALLPM, our segment registers will
  158. ;have been loaded by XLIBE.  They will be set as follows:  CS will have
  159. ;TSEG base and a 4Gb limit (Gb = gigabyte).  SS will also have TSEG base and
  160. ;a 4Gb limit; however, code segments and data segments are distinguished in
  161. ;protected mode.  We will have 4096 free bytes on the stack.  DS will have a
  162. ;zero base and 4Gb limit.  This is what is called a "flat" segment.  ES will
  163. ;have TSEG base and a 4Gb limit (same as SS).  FS will have DSEG base and a 
  164. ;64K limit.  DSEG is the data segment used by XLIB.  Finally, GS will have 
  165. ;DGROUP base and a 64K limit.  DGROUP is the segment group created by the 
  166. ;.DATA directive.  If you are a high-level language programmer, then DGROUP
  167. ;is also where your compiler probably puts all of the near data.
  168. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  169.  
  170. TSEG           SEGMENT PARA PUBLIC USE32 'CODE'
  171.                ASSUME CS:TSEG, SS:TSEG, ES:TSEG, FS:DSEG, GS:DGROUP
  172.  
  173. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  174. ;   Now let's look at the PMMAIN procedure below.  You will see that it is a 
  175. ;near procedure.  This must be the case for all procedures called by CALLPM.  
  176. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  177.  
  178. PMMAIN         PROC NEAR
  179.  
  180. ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  181. ;   We are in 32-bit protected mode
  182. ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  183.  
  184. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  185. ;   By the way, you will get all registers except segment registers and ESP 
  186. ;at the values you had in them in real mode, including status flags (carry 
  187. ;flag, zero flag, etc) and the interrupt flag.  The stack got switched on 
  188. ;you, so don't try passing stack arguments through CALLPM.  If you have 
  189. ;arguments, then send them across in registers (e.g. load a register with a
  190. ;pointer to a structure containing all arguments).
  191. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  192.  
  193. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  194. ;   Of course, no language tool can be legitimate unless it can say "hello" 
  195. ;to the world, so let's take care of this important business now.  We will 
  196. ;use DOS function 02H for this purpose.
  197. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  198.  
  199.                MOV            EBX,OFFSET HELLOWWORLD
  200.                MOV            AH,02H
  201. GETCHAR:       MOV            DL,CS:[EBX]    ;Read with CS but don't write!!!
  202.                OR             DL,DL
  203.                JZ             GOODBYEWORLD
  204.                INT            21H            ;DOS interrupt
  205.                INC            EBX
  206.                JMP            GETCHAR
  207.  
  208. HELLOWWORLD    DB "Hello world!",0
  209.  
  210. GOODBYEWORLD:
  211.  
  212. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  213. ;   Did you forget that DOS is a real-mode system???  If not then you could
  214. ;be wondering how we got away with what we just did (i.e. INT 21H).  The
  215. ;answer is simple.  At this point, XLIBE is trapping all interrupts occurring 
  216. ;in protected mode.  It is then switching to real mode and relaying the call 
  217. ;to the corresponding real-mode interrupt handler.  Finally, it is switching 
  218. ;back to protected mode and returning control to us.  This is commonly called
  219. ;"reflection."  We call it "deflection" in XLIB.DOC.  Any interrupt will 
  220. ;continue to be deflected until you install your own protected-mode interrupt 
  221. ;handler for the interrupt.
  222. ;   Therefore, we can still use our favorite software interrupts with one 
  223. ;provision:  These interrupts cannot return values in the segment registers.  
  224. ;Such values would be overwritten with segment selectors on the way back to 
  225. ;protected mode.  More about segment selectors later.
  226. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  227.  
  228. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  229. ;   Now, let's get down to real business.  Your principal interest in 
  230. ;protected mode was likely access to extended memory.  Well, it is now there 
  231. ;for the taking.  However, we must play by the rules.  If you just start
  232. ;punching around in extended memory, then you could get a nasty thing called 
  233. ;a "page fault."  We will talk more about this later.  Also, somebody else 
  234. ;might be up there - like your disk cache or your ram drive.  Mess with those
  235. ;and you are going to feel real bad.
  236. ;   The safe approach is to allocate the extended memory with the PMGETMEM 
  237. ;function.  Just load EAX with the number of bytes you want and call 
  238. ;PMGETMEM.  Let's get 64K.
  239. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  240.  
  241.                MOV            EAX,10000H
  242.                CALL           PMGETMEM
  243.  
  244. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  245. ;   PMGETMEM will return with an error code in EAX.  If EAX = 0, then the
  246. ;address of the allocated block will be in EDX.  The actual size of the block
  247. ;will be in ECX, and a handle for the block will be in EBX.  The allocated
  248. ;size will always be at least as large as your request.  You will need the
  249. ;handle in EBX in the event that you later wanted to release the block; 
  250. ;however, there is little need to worry about this.  XLIBE will release it 
  251. ;for you automatically when you terminate.
  252. ;   Now, let's see if we got an error from PMGETMEM.  If so, then we will
  253. ;return to real mode using the near RET instruction; otherwise, we will zero
  254. ;the entire allocated block just for fun.
  255. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  256.  
  257.                OR             EAX,EAX
  258.                JZ             ZEROMEMORY
  259.                RET                           ;Go back to real mode
  260.  
  261. ZEROMEMORY:    SUB            ECX,4          ;ECX is always a multiple of 4
  262.                MOV            [EDX+ECX],EAX  ;EAX = 0 from PMGETMEM
  263.                JNZ            ZEROMEMORY
  264.  
  265. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  266. ;   The RET instruction is an easy way to get back home, but suppose you had
  267. ;pushed some stuff on the stack since the call to CALLPM.  Then RET would be
  268. ;potentially disastrous.  In this case you get back to real mode by jumping 
  269. ;to a procedure call RETPM.  The following instruction would do the trick:
  270. ;
  271. ;              JMP            RETPM
  272. ;
  273. ;You can use this instruction just about anywhere.
  274. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  275.  
  276. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  277. ;   Now, suppose you have a bug somewhere along here and you therefore wish 
  278. ;to look at your register values.  This is easy under XLIBE.  Just use the 
  279. ;INT 3 instruction.
  280. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  281.  
  282.                INT            3
  283.  
  284. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  285. ;   INT 3 generates a breakpoint exception.  XLIBE will trap this exception
  286. ;and print a report to the screen.  This report will contain register state
  287. ;and other useful information.  From the report screen you will be given the
  288. ;option to resume execution.  XLIBE will attempt to trap and report all CPU
  289. ;exceptions in this manner.  Expect to get plenty of them in protected mode.  
  290. ;Don't worry, they are not going to crash your machine all the time like they 
  291. ;did in the old days.  XLIBE is pretty good at cleaning up your mess.
  292. ;   See the special note below if you want to know how XLIBE is going to 
  293. ;handle your FPU exceptions.
  294. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  295.  
  296. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  297. ;   OK.  You already know enough about XLIBE to write some powerful code.
  298. ;You might not be interested in the remaining examples, so let's talk for a
  299. ;while about what you need to know concerning protected mode in general, and 
  300. ;then you can get to programming if you wish.
  301. ;   The principle difference between protected mode and real mode from the
  302. ;programmer's perspective is the interpretation of the segment registers.  In
  303. ;real mode the value in the segment register is multiplied by 16 and then
  304. ;added to an offset to get an address.  For example, consider:  MOV AX,[BX].
  305. ;This instruction implicitly uses the DS register for the memory access.
  306. ;Suppose DS = 02FFH and BX = 4004H, then AX would be loaded from 6FF4H (or 
  307. ;10H * 02FFH + 4004H).  It's simple, but it is also very limiting.
  308. ;   By the way, 4004H would be called the "effective" address while 6FF4H 
  309. ;would be called the "linear" address.  The "base" address of the segment is 
  310. ;simply the linear address at offset zero.  In this case, the base address is 
  311. ;2FF0H.
  312. ;   Now we will find out what stinks about real mode:  The largest value
  313. ;you could have in DS is FFFFH (64K - 1).  The largest offset you could have 
  314. ;is also FFFFH.  This means that the largest linear address you could get in 
  315. ;real mode is 10FFEFH.  That's 1 meg plus about 64K.  Isn't much these days.
  316. ;You wouldn't be reading this if it were.
  317. ;   In fact, you can't even get 10FFEF if you have only 20 address lines (as 
  318. ;in the 8086).  In this case you would be limited to FFFFFH, the largest 
  319. ;number that can be expressed with 20 bits.  That's where the one meg 
  320. ;limitation came in.
  321. ;   The addresses beyond FFFFFH and up to 10FFEFH are commonly called the
  322. ;"HMA" (high memory area).  You get them from real mode when you have got
  323. ;more than 20 address lines (as in the 286 or higher).  The "A20" you may 
  324. ;have heard about is the 21st address line (the first one is numbered zero).
  325. ;   In protected mode, the segment registers contain "selectors."  Selectors
  326. ;index system tables called "descriptor tables."  Under XLIBE, your selectors
  327. ;will be indexing the "local descriptor table" or LDT.  The entries in these 
  328. ;tables contain "descriptors."  These are each eight bytes long.  XLIBE sets 
  329. ;them all up for you.
  330. ;   The descriptors specify many things concerning a segment; however, two 
  331. ;are of special importance.  First, these descriptors specify the base 
  332. ;address of the segment.  Second, they specify the limit of the segment.  The 
  333. ;limit is the largest offset that can be used in conjunction with the 
  334. ;segment.  The nice thing about protected mode is that both the base and the 
  335. ;limit can be as large as FFFFFFFFH (that's 4Gb - 1).
  336. ;   One of the reasons that we use the term "protected mode" is the 
  337. ;enforcement of the limits on the segments.  Suppose you had DS loaded with
  338. ;a descriptor having FFFH limit, and suppose you tried to execute something
  339. ;like MOV AX,[1000H], then you will have violated protection rules and the
  340. ;processor will shut you down with an exception #13.  Hence, memory outside
  341. ;of the segment is somewhat protected.  Actually, there are other protection
  342. ;mechanisms far more important than this.  More later.
  343. ;   Observe that we could get mighty close to emulating real mode from within
  344. ;protected mode.  We could load segment registers with base addresses less
  345. ;than FFFFFH and fix their limits at FFFFH.  Then all we would need to do
  346. ;is make the processor interpret loads to the segment registers as changes
  347. ;to their base addresses (actually, a little more would be necessary).  In 
  348. ;fact this can be done.  It's called "virtual 8086 mode."  You are using it 
  349. ;right now if you have something like EMM386, 386MAX, or QEMM386 loaded.  You
  350. ;may be surprised to know that you have been using protected mode all along!
  351. ;   So, the moral to the above story is:  A segment register must never be 
  352. ;loaded with anything but a selector to a valid descriptor.  In real mode you 
  353. ;could always execute MOV DS,AX.  This isn't going to work in protected mode 
  354. ;unless AX contains a selector.  If it doesn't, then an exception #13 is 
  355. ;headed your way.
  356. ;   OK.  The next major difference between the two modes has already been
  357. ;mentioned:  In real mode, your offsets cannot be greater than FFFFH.  In
  358. ;protected mode they can be as great as your segment limit, and that usually
  359. ;means 4 gig.  By the way, there is nothing inherent to real mode that would
  360. ;logically prevent segment limits greater than FFFFH.  The processor imposes
  361. ;this limit to enforce emulation of the 8086.  If we could persuade the 
  362. ;processor to lift this restriction, then we would find ourselves with a 
  363. ;really powerful real mode.  In fact this can be done on the data segments,
  364. ;and is done in some software (e.g. HIMEM.SYS).
  365. ;   Perhaps you have heard of something called the "flat model" or 
  366. ;"unsegmented model."  This is a protected-mode model in which we set all 
  367. ;segment base addresses to zero and set all segment limits to FFFFFFFFH.
  368. ;This makes life real easy, and it's probably where we are headed in the 
  369. ;future.  XLIBE tries to get you as close to this model as it can so that you 
  370. ;won't have to be changing your code when your future finally gets here.
  371. ;   Now let's talk about 32-bit processing as opposed to 16-bit processing.  
  372. ;There are a few things you need to know.  Notice that we have "USE32" on the 
  373. ;TSEG segment declaration above.  This is why we call TSEG a 32-bit segment.
  374. ;Segments you have used in times past had the USE16 attribute (the default).  
  375. ;DSEG and DGROUP are also USE16 segments.  You have probably guessed that 
  376. ;USE16 gives you a 16-bit segment.  The difference derives from a peculiarity 
  377. ;of the processor.  For most instructions, the processor has both a 16-bit 
  378. ;version and a 32-bit version.  The peculiarity is that the op codes are 
  379. ;generally the same.  Consider PUSH AX as opposed to PUSH EAX.  Would you 
  380. ;believe that they are encoded exactly the same?  The op code is 50H either 
  381. ;way.
  382. ;   So how do we tell the difference?  Well, it's done with a single bit in 
  383. ;the code segment descriptor.  If this bit is set, then the instruction will 
  384. ;be interpreted as having a 32-bit operand.  It will be interpreted as a 
  385. ;16-bit instruction otherwise.  This bit is called the "D bit" (default 
  386. ;operand/address size).  
  387. ;   So, what if you really wanted a PUSH AX even when this curious bit is 
  388. ;set?  You do this by encoding an "operand prefix" in front of the 
  389. ;instruction.  The prefix is 66H.  So if we want PUSH AX from within 32-bit 
  390. ;mode, then we encode the instruction as 66H followed by 50H.  Conversely, if 
  391. ;we wanted PUSH EAX in 16-bit mode, then we must also encode it as 66H 
  392. ;followed by 50H.  Thus, the 66H temporarily places the processor in the 
  393. ;opposite mode dictated by the D bit.
  394. ;   The USE32 directive informs the assembler that we intend to execute in 
  395. ;the declared segment with the D bit set.  This information lets the 
  396. ;assembler know that it must stick operand prefixes in front of 16-bit 
  397. ;instructions.  Accordingly, USE16 tells the assembler that we are going
  398. ;to execute in the declared segment with the D bit clear.  Now the assembler
  399. ;must stick the operand prefix in front of 32-bit instructions.  
  400. ;   A somewhat different situation exists for base registers and index 
  401. ;registers, but the conclusions are still the same.  Consider MOV AL,[SI] 
  402. ;and MOV AL,[ESP].  The first is encoded as 8AH 04H, the second is encoded
  403. ;as 8AH 04H 24H.  When the processor sees the last sequence, how can it tell
  404. ;whether it reading MOV AL,[ESP], or MOV AL,[SI] plus the first byte in the 
  405. ;next instruction?  It can't, or at least not without a prefix.  The prefix 
  406. ;in this case is 67H.  In a 32-bit segment, a 67H indicates that a 16-bit
  407. ;register is being used for a base or index.  In a 16-bit segment, 67H
  408. ;indicates a 32-bit base or index.  The USE32 and USE16 directives tell the 
  409. ;assembler where to insert 67H.
  410. ;   What if we had MOV AX,[BX] in a 32-bit segment?  Then we must encode both
  411. ;prefixes (67H must be first).
  412. ;   You have probably gathered that 16-bit instructions are bad in a 32-bit
  413. ;segment.  They obviously require more memory.  What isn't obvious is that
  414. ;they burn up CPU time (typically 1 clock each on the 486).  So avoid 16-bit 
  415. ;registers while in TSEG.  8 bit registers are OK in either kind of segment.
  416. ;   Observe that the D bit has little to do with protected-mode.  We could
  417. ;run a protected-mode program in a 16-bit segment, but then we have to pay
  418. ;a price for using 32-bit registers.  That just isn't smart.  The only 
  419. ;association between the D bit and protected mode is that you must be in
  420. ;protected mode if the D bit is set.  There is no such thing as 32-bit
  421. ;real mode, even though it is conceptually possible.
  422. ;   OK.  There is one other thing that you should perhaps understand.  Its
  423. ;called "page mode," but the concept is a little complicated, so we have 
  424. ;placed it at the bottom of this file.  You can read it if you like, but
  425. ;will be OK without it.
  426. ;   If you are ready to leave, then we can terminate our program with the
  427. ;following lines:
  428. ;
  429. ;
  430. ;              RET                           ;Go back to real mode
  431. ;PMMAIN        ENDP
  432. ;
  433. ;TSEG          ENDS
  434. ;              END
  435. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  436.  
  437. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  438. ;   Back to our discussion about XLIBE.  From now on we are going to deal 
  439. ;with you bad guys.  
  440. ;   Let's suppose you have an IO device which maps to extended memory 
  441. ;addresses.  You need to read or write to these addresses.  The memory is 
  442. ;supplied by the device and is not recognized as ordinary RAM by the 
  443. ;operating system.  The operating system may not even know that it is there.  
  444. ;Let's suppose that the device map begins at the 16th meg boundary (1000000H) 
  445. ;and is 64K long.  
  446. ;   It would not be wise just to start poking and peeking around 1000000H.
  447. ;There is a problem in that your device maps to "physical" addresses whereas 
  448. ;your instruction code is using "logical" addresses.  They may not be the 
  449. ;same.  See the discussion about page mode below if you want to understand 
  450. ;the difference.
  451. ;   You need to create a logical address space for accessing the device.  
  452. ;This is very simple.  Just call PMMAPIO with EDX = the physical address of 
  453. ;the device and EAX = the size of the window.
  454. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  455.  
  456.                MOV            EDX,1000000H
  457.                MOV            EAX,10000H
  458.                CALL           PMMAPIO
  459.  
  460. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  461. ;   PMMAPIO returns an error code in EAX.  If EAX = 0, then EDX will equal 
  462. ;the starting address at which the device should be accessed.  We will check 
  463. ;out this error code before we continue.
  464. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  465.  
  466.                OR             EAX,EAX
  467.                JZ             MOREHARDCORE
  468.                RET                           ;Back to real mode
  469. MOREHARDCORE:
  470.  
  471. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  472. ;   OK.  Now let's suppose you have a great big database on disk that you 
  473. ;have never been able to completely read into memory because of DOS 
  474. ;limitations.  We are fixing to load the whole thing.  First, we must 
  475. ;allocate memory to contain the file.  Let's do that now.  We are going to
  476. ;assume that the file will never be larger than one meg, but you could 
  477. ;increase the assumed limit if you wanted.
  478. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  479.  
  480.                MOV            EAX,100000H    ;Get 1 Meg of memory for file
  481.                CALL           PMGETMEM
  482.                OR             EAX,EAX        ;See if an error occurred
  483.                JZ             WAYDOWN
  484.                RET                           ;Back to real mode
  485.  
  486. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  487. ;   Next, we need to set up a file control block (not a DOS FCB) to describe 
  488. ;the file to XLIBE.  XLIBE will expect the control block to be in the form of 
  489. ;the structure below.
  490. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  491.  
  492. XFILE          STRUCT
  493.   CONDCODE     DWORD          0          ;Condition code from file operation
  494.   FNAME        BYTE  68 DUP(0)           ;ASCII file path (zero terminated)
  495.   FHANDLE      WORD           0          ;File handle assigned by DOS
  496.   FPTRMODE     WORD           0          ;File pointer reference
  497.   FPTR         DWORD          0          ;Initial file pointer
  498.   BLOCKADR     DWORD          0          ;Memory source/destination address
  499.   BLOCKSIZE    DWORD          0          ;Size of transfer block in bytes
  500.   BUFFERADR    DWORD          0          ;Address of memory buffer in 1st meg
  501.   BUFFERSIZE   WORD           0          ;Buffer size in bytes
  502.   CONTROL      WORD           0          ;Control word
  503. XFILE          ENDS
  504.  
  505. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  506. ;TASM Only:
  507. ;   If you are a TASM programmer, then don't use the structure above.  Use:
  508. ;
  509. ;XFILE         STRUC
  510. ;  CONDCODE    DD             0
  511. ;  FNAME       DB  68 DUP(0)
  512. ;  FHANDLE     DW             0
  513. ;  FPTRMODE    DW             0
  514. ;  FPTR        DD             0
  515. ;  BLOCKADR    DD             0
  516. ;  BLOCKSIZE   DD             0
  517. ;  BUFFERADR   DD             0
  518. ;  BUFFERSIZE  DW             0
  519. ;  CONTROL     DW             0
  520. ;XFILE         ENDS
  521. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  522.  
  523. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  524. ;Let's suppose that the filename is JUNK.DAT and that this file is in the 
  525. ;current directory.  If you want to run this program, then create a small 
  526. ;text file called JUNK.DAT in the current directory.  We are going to get
  527. ;the assembler to put our file name in the structure.  The rest we will punch
  528. ;in ourselves.  Remember that EDX is the linear address of the memory block
  529. ;where we are going to load this file.  ECX is the size of the block.  We
  530. ;obtained these values from PMGETMEM above.  Also, remember that ES is loaded
  531. ;with a selector to TSEG.
  532. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  533.  
  534.                ALIGN          4              ;Unaligned data will slow you down
  535. FCB            XFILE          <0,"JUNK.DAT"> ;File control block
  536.  
  537. WAYDOWN:       MOV            ES:FCB.BLOCKADR,EDX
  538.                MOV            ES:FCB.BLOCKSIZE,ECX
  539.                MOV            ES:FCB.BUFFERSIZE,0H
  540.  
  541. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  542. ;   OK. Lets get the file and talk about it later.  We will need to call
  543. ;PMXLOAD.  PMXLOAD will expect EAX to contain the linear address of the file
  544. ;control block.  Here we go.
  545. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  546.  
  547.                MOV            EAX,TSEG       ;Get segment of control block
  548.                SHL            EAX,4          ;Multiply by 16 and add offset
  549.                ADD            EAX,OFFSET FCB ;EAX = linear address now
  550.                PUSH           EAX            ;We will use this again
  551.                CALL           PMXLOAD
  552.  
  553. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  554. ;   Now, we will explain what we just did.  BLOCKADR is the address of the 
  555. ;memory block where we want the file to be loaded.  BLOCKSIZE is the size of 
  556. ;this block.  PMXLOAD will not load past the end of the block since that 
  557. ;could be very dangerous to you or whoever else might be in memory.  Now, DOS
  558. ;is going to access the disk for us, but DOS can't work with extended memory.
  559. ;So, we set up a buffer in conventional memory for DOS.  PMXLOAD will handle
  560. ;the transfers from the buffer to extended memory.  You could supply your 
  561. ;own buffer address and size in BUFFERADR and BUFFERSIZE; however, if you
  562. ;will just set BUFFERSIZE to zero, then XLIBE will supply its own buffer.
  563. ;   PMXLOAD will return an error code in both EAX and CONDCODE (same error
  564. ;code).  We will check it now.
  565. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  566.  
  567.                OR             EAX,EAX
  568.                JZ             GOTFILE
  569.                JMP            RETPM          ;Back to real mode
  570. GOTFILE:
  571.  
  572. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  573. ;   Notice that if an error occurs, then we transfer control back to real 
  574. ;mode with JMP RETPM rather than RET.  This is because we've got some stuff
  575. ;on the stack now; namely, the linear address of our file control block.  Of
  576. ;course a POP EAX followed by a RET would still work.
  577. ;   OK.  Suppose you play around with memory image of the file, perhaps 
  578. ;making a few modifications.  Now you want to save it.  The file control 
  579. ;block should be defined as before, except BLOCKSIZE and BLOCKADR should
  580. ;specify the address and size of the memory block you are going to save.
  581. ;PMXLOAD modified BLOCKSIZE in the file control block.  It was set to the
  582. ;actual size of the file that it loaded.  This means that unless you have
  583. ;changed the size of the memory image, then you need to make no further 
  584. ;modifications to the file control block to perform the save.  Just call
  585. ;PMXSAVE with the address of the control block in EAX.  Here we go.
  586. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  587.  
  588.                POP            EAX            ;Get FCB address
  589.                CALL           PMXSAVE
  590.                OR             EAX,EAX        ;See if an error occurred
  591.                JZ             FILEISSAVED                                      
  592.                RET
  593. FILEISSAVED:
  594.  
  595. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  596. ;   XLIBE can also perform random reads and writes with files.  See the 
  597. ;manual if you want to know more.
  598. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  599.  
  600. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  601. ;   One more topic.  This concerns interrupt handlers.  These are real devils
  602. ;in any mode.  Unfortunately, XLIBE can't simplify matters as much as it would
  603. ;like.  This is because XLIBE must work in conjunction with other software
  604. ;(VCPI or DPMI) to give you all of this protected-mode stuff.  This other
  605. ;software doesn't handle interrupts the same, and there isn't much that XLIBE
  606. ;can do to smooth out the lumps.  You better read the manual on this one.
  607. ;   Anyway, we are going to install a simple timer-tick interrupt which will
  608. ;print an asterisk to the screen with each timer tick.  We are going to use
  609. ;routines in the file PMIO.INC to do the printing.  You will see that we have
  610. ;INCLUDE PMIO.INC several lines down in the code.
  611. ;   PMIO.INC is a very handy little file and is very simple to use.  Examine
  612. ;the file if you want to know more.  The comments in the file explain how to
  613. ;use the PMIO procedures.  There are two things you need to know for now:
  614. ;PMIO.INC must be included in TSEG and its procedures should always be called
  615. ;with DS loaded with a flat selector (as it is at present).
  616. ;   Let's clear the screen before we get started with our interrupt handler.
  617. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  618.  
  619.                CALL           CLS
  620.  
  621. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  622. ;   OK.  We are going to install our own protected-mode interrupt handler for
  623. ;the timer tick.  The timer tick is a hardware interrupt on IRQ 0.  First, we
  624. ;need to get the current protected-mode interrupt vector so we can restore it
  625. ;when we are done.
  626. ;   Now the timer tick is generally at vector eight (INT 8), but we must deal
  627. ;with the possibility that XLIBE has remapped interrupts.  We can't assume that
  628. ;IRQ 0 is still assigned to INT 8.  The current mapping is contained in the
  629. ;XLIBE data area (DSEG) in a variable called IRQ0INTNO.  Let's get it now.
  630. ;Remember that FS contains a selector for DSEG.
  631. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  632.  
  633.                MOV            AL,FS:IRQ0INTNO
  634.  
  635. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  636. ;   OK.  To get the current vector, we need merely to call PMGETPMIV (get
  637. ;protected-mode interrupt vector) with the vector number in AL.  The address
  638. ;of the current interrupt handler will be returned in CX:EDX.  Here we go.
  639. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  640.  
  641.                CALL           PMGETPMIV      ;No error code from this call
  642.                PUSH           ECX            ;Save handler address on stack
  643.                PUSH           EDX
  644.  
  645. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  646. ;   Now we will set our new vector.  We must call PMSETPMIV with AL = the
  647. ;interrupt number and CX:EDX = the new handler address.  CX must be a segment
  648. ;selector.  Our new handler is called TIMERINT.  It is located just a few 
  649. ;lines down.  We will get there in just a moment.  PMSETPMIV will return an
  650. ;error code in EAX.  We will check this, as always.
  651. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  652.  
  653.                PUSH           CS             ;Put handler address in CX:EDX
  654.                POP            ECX                             
  655.                MOV            EDX,OFFSET TIMERINT                              
  656.                CALL           PMSETPMIV
  657.                OR             EAX,EAX        ;Check error code
  658.                JZ             WATCHTICKS
  659.                JMP            RETPM          ;Back to real mode
  660. WATCHTICKS:
  661.  
  662. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  663. ;   Notice that we treated PUSH CS as though it put 4 bytes on the stack.
  664. ;It did.  In 32-bit mode, the processor tries to keep the stack aligned at
  665. ;MOD 4.  So, it padded the PUSH with a two-byte zero.  It will always do
  666. ;this with segment registers (on far calls, on interrupts, on everything), 
  667. ;and you would do well to remember it.
  668. ;   OK.  Now we need a little loop to delay long enough for a few timer
  669. ;ticks to happen.  There are about 18.2 of them in a second.  If you have
  670. ;a 486-33, then the next loop will work fine.  If you have a 386, then you
  671. ;may be watching ticks for a while.
  672. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  673.  
  674.                MOV            ECX,0FFFFFFH
  675. WAITAWHILE:    LOOP           WAITAWHILE     ;Asterisks are being printed
  676.  
  677. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  678. ;   That's enough.  Let's put the old vector back.
  679. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  680.  
  681.                POP            EDX
  682.                POP            ECX
  683.                MOV            AL,FS:IRQ0INTNO
  684.                CALL           PMSETPMIV      ;Will ignore any error here.
  685.                                              ;We couldn't do anything else.
  686.  
  687. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  688. ;   Here is where we go back to real mode for good.  But keep on reading.       
  689. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  690.  
  691.                RET                           ;Goodbye protected, ole pal
  692.  
  693. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  694. ;   Now, let's look at our protected-mode handler.  Comments are below.
  695. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  696.  
  697. TIMERINT       PROC FAR
  698.                STI
  699.                PUSH           EAX
  700.                PUSH           DS             
  701.                MOV            DS,CS:CSDSEGSEL  ;Load DSEG selector
  702.                MOV            DS,FLATDSEL      ;Load flat selector for PMIO
  703.                MOV            AL,"*"
  704.                CALL           PCH              ;Print AL as ASCII
  705.                CLI
  706.                MOV            AL,20H           ;Send EOI to 8259 master
  707.                OUT            20H,AL
  708.                POP            DS
  709.                POP            EAX
  710.                IRETD                           ;Don't use IRET in 32-bit segs
  711. TIMERINT       ENDP                            ;under MASM
  712.  
  713. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  714. ;   As you probably know, we are not supposed to assume anything about 
  715. ;anything in an interrupt handler, including segment register settings.  Now
  716. ;XLIBE has recorded our selector values in DSEG (see manual).  However, we
  717. ;have a little problem here:  We need the DSEG selector to get them.  The
  718. ;way we circumvent this dilemma is by putting a copy of the DSEG selector in
  719. ;your code segment where you can always read it.  XLIBE has already done
  720. ;this for you.  It placed the selector in CSDSEGSEL.
  721. ;   After we have loaded DS with DSEGSEL, we then load it with the flat 
  722. ;selector.  This is recorded in FLATDSEL.
  723. ;   You might be wondering why we didn't use a DOS function to do our 
  724. ;printing.  It's because you can bring a system down real hard and real fast
  725. ;by calling DOS in an interrupt handler.  The problem occurs when DOS is
  726. ;itself interrupted.  DOS is not generally reentrant.  The print routines in
  727. ;PMIO are very robust.  They are also fast.
  728. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  729.  
  730. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  731. ;  OK.  You are hopefully an XLIB programmer now.  Remember that XLIB is not
  732. ;free.  You must register it once you have developed any program with it.
  733. ;The fee is only $40 ($60 with technical support).  If you think that is bad,
  734. ;then you should look at the retail DOS extenders.  You are talking about
  735. ;a lot more money and a lot more work.  We appreciate your business
  736.  
  737. ;                                                 TechniLib
  738. ;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  739.  
  740. PMMAIN         ENDP
  741.  
  742.                INCLUDE        PMIO.INC       ;Protected-mode IO routines
  743.  
  744. TSEG           ENDS
  745.                END
  746.  
  747. ;*****************************************************************************
  748. ;                              Special Notes
  749. ;*****************************************************************************
  750.  
  751. ;                      ++++++++++++++++++++++++++++
  752. ;                      +     Writing Libraries    +
  753. ;                      ++++++++++++++++++++++++++++
  754.  
  755.  
  756. ;   You can give high-level language programs tremendous power by linking 
  757. ;them with protected-mode libraries developed with XLIBE.  See the EASYX.ASM 
  758. ;file for an example.
  759.  
  760.  
  761. ;                          ++++++++++++++++++++                                 
  762. ;                          +     Page Mode    +
  763. ;                          ++++++++++++++++++++
  764.  
  765.  
  766. ;   We discuss something here that's a little technical, but extremely
  767. ;enlightening.  It's called "page mode," available only in protected mode or
  768. ;in virtual 8086 mode, which is of course a subset of protected mode.  In
  769. ;page mode, memory addresses specified in instruction code undergo a little
  770. ;mathematical translation before they are actually applied to the physical 
  771. ;memory.  So what you ask for isn't always what you get.  For example, if
  772. ;you write something to address 10000H, it might actually go to 20000H.  This
  773. ;isn't bad as long as when you read from 10000H you also get 20000H, and
  774. ;indeed you will.  Incidentally, the address you specify is called a 
  775. ;"logical" address.  The address you actually get is called the "physical"
  776. ;address.
  777. ;   Now, a lot of mysteries may have just been explained to you.  For 
  778. ;example, you know that most of your programs must reside in the first meg.
  779. ;You also know that many of your programs consume most of the available 
  780. ;memory in the first meg.  How then do DESQview, Windows, OS/2, etc. run all
  781. ;of these programs at the same time???  Well, you give each program a 
  782. ;different translation formula.  Stick the first program in the first meg and
  783. ;give it one-to-one translation.  No trickery here.  Stick the second program
  784. ;in the second meg and then add 1 meg (100000H) to every address it tries to 
  785. ;read and write.  Remember, this program thinks it's in the first meg, so
  786. ;that's where it's going to be reading and writing.  But, because of the 
  787. ;translation, it's going to be getting the second meg.  This program has been 
  788. ;fooled.  It has been hornswoggled by page mode.
  789. ;   Perhaps you have wondered how your memory manager put your device drivers
  790. ;and TSRs in upper memory where ROM is supposed to be.  The answer is page 
  791. ;mode again.  The device drivers and TSRs are actually in extended memory.
  792. ;The logical addresses which would otherwise belong to the ROM area have been
  793. ;translated to these extended memory addresses.
  794. ;   Before explaining what all this has to do with you, let's explain the
  795. ;translation method.  The logical address you specify is of course 32 bits 
  796. ;wide.  We are going to divide these three ways:  the 10 upper bits, the 
  797. ;next ten bits, and the lowest 12 bits.  The translation runs thus:  The 10 
  798. ;highest bits are used as an index to a table which has 1024 entries.  These 
  799. ;entries are 4 bytes wide so the table consumes 4K.  These entries are 
  800. ;physical addresses to other tables which are also 4K in size.  We use your 
  801. ;upper 10 bits to pick the appropriate table.  Your next 10 bits will be used 
  802. ;as an index to the second-level table.  The entries in this table supply the 
  803. ;upper 20 bits to the physical address you are actually going to access.  The 
  804. ;lowest 12 bits of the physical address are supplied by the lowest 12 bits in 
  805. ;your logical address.  A mess isn't it.  Don't worry, you are not 
  806. ;responsible for handling this mess, but sometimes XLIBE is.
  807. ;   Now, the second-level table supplies all but the 12 lowest bits of the
  808. ;physical memory address; therefore, the addresses in this table are spaced 
  809. ;in memory at least 4096 bytes apart (2 ^ 12 = 4096).  The memory is 
  810. ;effectively divided into 4K units along 4K boundaries.  These 4K units are 
  811. ;called "pages," hence the term "page mode."
  812. ;   The first-level table is called a "page directory."  The tables at the 
  813. ;second level are called "page tables."  You typically give each program its 
  814. ;own page directory and page tables.  This is how we use a different 
  815. ;translation method for each program.
  816. ;   One of the most effective ways to protect a physical page from a
  817. ;particular program is to simply avoid placing the address of the page in the 
  818. ;program's page tables.  If the address of a physical page is not anywhere
  819. ;in the page tables, then there is no way that the program can access it,
  820. ;provided of course that we also prevent the program from playing with the 
  821. ;page tables themselves.  Hence, we could completely shield one program from
  822. ;another.  This largely explains how OS/2 and Windows NT achieve such high
  823. ;degrees of protection.
  824. ;   Now the page tables are themselves pages.  That is, they are 4K in size
  825. ;and are situated on 4K physical address boundaries.  This means that their
  826. ;addresses are zero in the lowest 12 bits.  The pages they catalog are also
  827. ;zero in the lowest 12 address bits.  This means that for each entry in the 
  828. ;page directory and for each entry in the page tables we have 12 bits to play 
  829. ;with (the lowest 12) because we never need them for address specification.
  830. ;   An operating system can use certain of these bits to implement other
  831. ;protection mechanisms of the processor.  For example, the operating system 
  832. ;can use one of these bits to designate the page as read-only.  Try to write 
  833. ;to it and you are going to get a page fault (exception #14).  A page can 
  834. ;also be marked as privileged.  This means that the operating system can
  835. ;access it but most other programs cannot.  
  836. ;   Another important bit in the page tables is the "present" bit.  This bit
  837. ;is set if there is actually a physical page available for the implied 
  838. ;logical address.  What happens when you try to access a logical address
  839. ;whose page table entry is marked not present?  Well, you are going to get
  840. ;another page fault.  Under XLIBE this almost certainly means that your
  841. ;program is going to get terminated.  Indeed, XLIBE will usually be 
  842. ;responsible for terminating you (gently if at all possible).  Which explains
  843. ;one of the reasons why you need to understand page mode.  Page faults are
  844. ;going to become a major part of your protected-mode life.  If your program
  845. ;has a wild read or write, or a bad JMP or CALL, or a RET or IRET with
  846. ;a mismanaged stack, then page faults are likely.  
  847. ;   Some operating systems and memory managers have "virtual memory."  This
  848. ;is where you are using disk to emulate memory.  The operating system may
  849. ;give you a logical address block which really doesn't have any physical
  850. ;memory to back it up.  The page table entries for this logical address block
  851. ;are all marked not present.  When you try to access one of these logical 
  852. ;addresses, the resultant page fault is trapped by the operating system, 
  853. ;which then pulls the requested page off the disk and places it in a special 
  854. ;memory area designed for such purposes.  Your page table entry is then 
  855. ;pointed to this location and is marked present.  Finally, control is 
  856. ;returned to you.  You may never know what happened.
  857. ;   You can actually use virtual memory under XLIB.  Just get a DPMI host
  858. ;with virtual memory capabilities.
  859.  
  860.  
  861.                         +++++++++++++++++++++++
  862.                         +    FPU Exceptions   +
  863.                         +++++++++++++++++++++++
  864.  
  865.  
  866. ;   Floating point exceptions are handled a bit differently than CPU 
  867. ;exceptions by XLIBE.  Suppose you had a major goofup in your code like this:
  868. ;
  869. ;              FLD1                          ;Load 1.0
  870. ;              FLDZ                          ;Load 0.0
  871. ;              FDIV                          ;Compute 1.0/0.0 (very sinful)
  872. ;              FSTP           ST             ;Pop the "quotient"
  873. ;
  874. ;The FDIV will cause a zero divide exception in the FPU.  This exception will
  875. ;not be signalled to the CPU until the next floating point instruction.  So
  876. ;the FSTP ST instruction is where things are going to happen.
  877. ;   XLIBE is going to promptly throw you back into real mode when this 
  878. ;happens.  As explained above, XLIBE remembers where you left real mode (it
  879. ;kept the return address from the call to CALLPM), and that is where it is
  880. ;going to send you.  When you get there, everything but EAX and EDX are going
  881. ;to be restored to the values they last had in real mode.  AX will contain
  882. ;an error code.  The high word of EAX will contain the FPU status word.  You
  883. ;can examine this to determine what caused the exception.  Your machine may
  884. ;be in an unstable state (not likely).  Read the documentation for more.
  885.