delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1999/02/06/11:08:13

Message-ID: <36BC6B22.232C2A53@sbg.at>
Date: Sat, 06 Feb 1999 17:17:39 +0100
From: "Michael Burian" <michael DOT burian AT sbg DOT at>
X-Mailer: Mozilla 4.5 [en] (Win95; I)
X-Accept-Language: en, de-AU,de
MIME-Version: 1.0
To: djgpp AT delorie DOT com
Subject: Re: protected mode interrupts
References: <36BC2FDF DOT 62D0F9B0 AT gmx DOT de>
Reply-To: djgpp AT delorie DOT com

This is a multi-part message in MIME format.
--------------07F6BF90C0A33425CE25AEB4
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit



Christian Hofrichter wrote:

> I want to install an interrupt handler via the dpmi-functions.
> As I have not much experience in programing assembler interrupt
> routines, I would be pleased if anyone could tell me where I can find
> some documentations or tutorials about  protected mode interrupts and
> the dpmi-function.
>
> The dpmi-specification does not say anything special about memory
> locking for interrupts. Do I have only to lock the data area which is
> touched by the int-handler ?
> Or do I have also to lock the code area of the int-handler ???
> Does an int-function only differ from an ordinery function by the fact
> that it ends with a sti and an iret.

is that what you need?
<djint.txt>


             The Dark Art of writing DJGPP Hardware Interrupt Handlers

                        (Which might conceivably be useful
                            for other DPMI environments)

                                    Version 1.0

             (C) Alaric B. Williams (alaric AT abwillms DOT demon DOT co DOT uk)
             Copy in unmodified form, as long as you give all due
             credit.

             PREFACE

             This tutorial assumes an understanding of what a hardware
             interrupt is for, and how it works, along with the usual
             programming skills. Ideally, you will have coded a
             hardware interrupt handler in real mode DOS before.

             No guarantee of the correctness of information in this
             tutorial is given; if any errors are found, please report
             them to alaric AT abwillms DOT demon DOT co DOT uk, and I will fix them
             for the next release. I cannot accept responsibility for
             any damages caused as a result, direct or indirect, of
             using the information presented herein.

             Anyway, onto the interesting parts!

             INTRODUCTION

             Hardware interrupt handling under DPMI is not all that
             different to the same under real mode. There are just a
             few more things to consider.

             All interrupts in DPMI protected mode go through the DPMI
             host, first of all. The DPMI host tells the CPU to stop
             using the normal interrupt vector table at real mode
             address 0000:0000, by setting the 386+ IDT (Interrupt
             Descriptor Table) register to a vector table somewhere
             else in memory.

             The DPMI host also maintains a 'virtual interrupt vector
             table', which lists the addresses of protected mode
             handlers. When an interrupt comes in, the DPMI host scans
             it's internal table; if that interrupt number is claimed
             by a protected mode function, it switches to protected
             mode if the CPU is in real mode (for example, when you
             use getch(), the CPU is running the real mode getch()
             routine in the BIOS), and simulates the interrupt. If the
             interrupt is not claimed by protmode, it uses the
             interrupt vector table in DOS memory to perform the jump
             to a real mode handler, after first making sure the CPU
             is in real mode.

             For example, if our protected mode application is working
             away, and the timer tick interrupt goes off (IRQ 0, INT
             8):












             1) CPU stores protected mode state and looks up the
             address to jump to in the IDT.

             2) As expected, it jumps to the DPMI host's interrupt
             wrapper for INT8.

             3) Wrapper checks to see if its interrupt has been
             claimed by a protected mode routine.

             4) It hasn't, so the wrapper switches to real mode, and
             jumps to the address stored in interrupt vector table
             entry 8, thus executing the real BIOS interrupt handler.

             5) Upon return from the BIOS handler, the wrapper cleans
             up after itself, returning to protected mode and
             restoring the machine state so our protected mode
             application continues to run smoothly.

             Evidently, a lot of switches between protected mode and
             real mode can be incurred; the DPMI host keeps the
             workings of this hidden from us (thankfully!), but the
             performance hit can be significant. Think twice about
             installing a timer tick interrupt at 20Khz!

             <RANT>

             While we're on the topic, I'd like to dispel a myth.
             People are often heard moaning "Oh! Protmode's so slow!
             All that switching about, just to call a DOS interrupt,
             or whenever the user presses a key and invokes IRQ1, the
             keyboard interrupt! I'll stick to real mode programming,
             thankyou!".

             Well, if you hear them saying that, snigger quietly, and
             let them write their 'fast' real mode games, because
             yours will quite likely run faster. Think about it: most
             PCs do not run in real mode at all these days; it's all
             V86 mode, if they're running any kind of 386 memory
             manager, DPMI host, or Windows product; every time an
             interrupt occurs, their DPMI/VCPI/whatever host takes a
             slice of time to see if any protected mode code wants
             that interrupt. And you get the advantages of large flat
             virtual memory spaces, coupled with more control about
             where your interrupts go. A DPMI application that's
             really hungry for speed can install a protected mode
             interrupt, or place a real mode routine in DOS memory
             hooked to the real mode handler, depending on where their
             program spends the most time. If it's a DOS I/O bound
             application, the real mode handler will run faster, as it
             does not incur a mode switch; and if it's a compute bound
             application, sitting in protected mode, you can write a
             true protected mode handler, which will execute sooner
             than a real mode one.

             </RANT>

             LOCKING











             The second important thing to consider with DPMI
             interrupt handlers is the vexed issue of paging. DPMI
             memory (in which your DJGPP application executes) can be
             paged out onto disk, making 'virtual memory' possible.
             Normally, if any 'paged out' RAM is requested, the DPMI
             host can bring it back in invisibly; but this cannot be
             done during the execution of an interrupt handler. If an
             interrupt handler, any other functions it invokes, or any
             variables it touches, is 'paged out', your code will bomb
             out with a page fault. The solution is to 'lock' the
             memory regions that must be available, telling the DPMI
             host to keep them in active memory at all times. This can
             be done with the DJGPP library functions
             _go32_dpmi_lock_code(void *,unsigned long) and
             _go32_dpmi_lock_data(void *,unsigned long), which each
             take a pointer and a length, specifying the block of
             memory to be locked.

             Before installing your handler, lock all code and data it
             touches. To lock a static variable, use:

             _go32_dpmi_lock_data(&my_var, sizeof(my_var));

             To lock a dynamically allocated memory block, you must
             know its size:

             char *buffer = malloc(buflen);
             assert(buffer);
             _go32_dpmi_lock_data(buffer,buflen);
             _go32_dpmi_lock_data(&buffer,sizeof(buffer));

             Don't forget to lock the pointer variable, as well as the
             data!

             Locking code is slightly different; we can't sizeof() a
             function, so we have to use a trick:

                  void my_handler()
                  {
                  }

                  void lock_my_handler()
                  {
                    _go32_dpmi_lock_code(my_handler, (unsigned
             long)(lock_my_handler - my_handler));
                  }

             (Copied directly from the info page). In other words,
             declare a function after your handler - hopefully a
             dedicated dummy function, in case you forget later on and
             move the function you're using as a marker - and use
             pointer arithmetic to deduce the code size of your
             handler function, while praying that gcc puts the
             functions in the order you specify!

             The Allegro games programming library uses a set of
             convenient macros to automate this. They are:











             #define END_OF_FUNCTION(x) void x##_end() { }

             #define LOCK_VARIABLE(x)  \
                 _go32_dpmi_lock_data((void*)&x, sizeof(x))

             #define LOCK_FUNCTION(x)  \
                 _go32_dpmi_lock_code(x, (long)x##_end - (long)x)

             LOCK_VARIABLE is pretty self explanatory; END_OF_FUNCTION
             is used like so:

             void my_function() {
                ...
             }

             END_OF_FUNCTION(my_function)

             and creates a dummy function named my_function_end.

             LOCK_FUNCTION uses this to expand:

             LOCK_FUNCTION(my_function)

             into:

             _go32_dpmi_lock_code(my_function, (long)my_function_end \
                    - (long)my_function);

             thus taking care of the paperwork for you.

             Perhaps the easiest and safest method of locking I have
             seen is to tell GCC to put everything you want locked
             into a special section of the COFF executable, which can
             then be locked all in one go. This is accomplished using
             the GCC "__attribute__ ((section "name"))" feature;
             however, in order to make this work (with the current
             version of DJGPP, 2.00), you have to download the GCC
             source code and fix a little bug. This fix is the
             brainchild of Bill Currie, and consists of adding the
             lines:

             #define ASM_OUTPUT_SECTION_NAME(FILE,DECL,NAME)\
             do { \
                fprintf(FILE,"\t.section %s\n",NAME); \
             } while(0)

             to the file "configure/i386/go32.h" in the gcc source,
             then recompiling.

             You also need to modify /lib/djgpp.lnk to tell the linker
             where to put these special 'locked' sections. I have
             written a library to do all this for you - "libints". It
             should be included with this document. See the sample
             code (in the directory /src/libints) for instructions.

             THE ACTUAL ACT OF INTERRUPT CLAIMING












             This is pleasantly painless. As GCC does not support an
             'interrupt' modifier on function names to tell them to
             provide the correct wrapper (with an IRET on the end),
             you must use a wrapper; but the DJGPP libc.a can do this
             for you in one fell swoop.

             If you merely want to chain onto a vector, and still have
             it passed back to the previous handler (which may be the
             original real mode one), simply use the following code:

             #define TIMER_INT 8

             void my_int_handler() {
               ...
             }

             ...

             _go32_dpmi_seginfo my_handler;
             my_handler.pm_offset = (int)my_int_handler;
             my_handler.pm_selector = _go32_my_cs();
             _go32_dpmi_chain_protected_mode_interrupt_vector( \
                       TIMER_INT, &my_handler);

             If you want to totally take an interrupt over, it gets a
             little more complex. Say we were installing the same
             handler - we would use:

             _go32_dpmi_seginfo my_handler;
             my_handler.pm_offset = my_int_handler;
             _go32_dpmi_allocate_iret_wrapper(&my_handler);
             _go32_dpmi_set_protected_mode_interrupt_handler( \
                         TIMER_INT, &my_handler);


             Since we've allocated a wrapper, we must, at some point:

             _go32_dpmi_free_iret_wrapper(&my_handler);

             Just make sure you've removed your interrupt handler
             before doing that!

             Please note: The wrapper DJGPP provides is not locked in
             memory, and is as such unreliable. Included in libints is
             a proper locked iret-wrapper system which is a little
             easier to use than the standard _go32_dpmi functions, and
             includes a useful function for simulating a protected
             mode interrupt, which makes it easy to chain to the
             original handler. This is more flexible than the GO32
             chaining  function used above, as you have the option of
             not chaining (useful for timer interrupts), and can do
             things like executing some code before chaining and some
             after. See the libints sample source for further
             instructions.

             If we want to preserve an interrupt vector, in order to
             restore it to its original value (which is kinda good
             practice!), we can simply use the code:











             _go32_dpmi_seginfo old_handler;
             _go32_get_protected_mode_interrupt_handler(TIMER_INT, \
                  &old_handler);

             ...

             _go32_set_protected_mode_interrupt_handler(TIMER_INT, \
                  &old_handler);

             EXAMPLE

             #include <go32.h>
             #include <dpmi.h>
             #include <sys/farptr.h>
             #include <string.h>
             #include <iostream.h>
             #include <dos.h>

             _go32_dpmi_seginfo old_handler,my_callback;

             volatile int counter;
             char *string;

             void my_int() {
                  counter++;
                  char *ch = string;
                  int addr = 0xA0000;
                  while(*ch) {
                       _farpokeb(_dos_ds,addr,*ch);
                       ch++;
                       addr += 2;
                  }
             }

             void end_int() {}

             void init_handler() {
                  _go32_dpmi_lock_data(&counter, sizeof(counter));
                  _go32_dpmi_lock_data(&string, sizeof(string));
                  _go32_dpmi_lock_code(my_int, (long)end_int - (long)
                                       my_int);
                  _go32_dpmi_get_protected_mode_interrupt_handler(8,
                                      &old_handler);
                  my_callback.pm_offset = (int)my_int;
                  _go32_dpmi_allocate_iret_wrapper(&my_int);
                  _go32_dpmi_set_protected_mode_interrupt_handler(8,
                                      &my_int);
             }

             void done_handler() {
                  _go32_dpmi_set_protected_mode_interrupt_handler(8,
                                      &old_handler);
                  _go32_dpmi_free_iret_wrapper(&my_int);
             }

             void update_text(char *s) {
                  int ints = disable();










                  string = s;
                  if(ints) enable();
             }

             main() {
                  counter = 0;
                  update_text("");
                  init_handler();

                  char buffer[80],temp[80];
                  _go32_lock_data(buffer,80);

                  cout << "Type some text followed by a CR,";
                  cout << " or EXIT to quit" << endl;
                  do {
                       cout << "Counter = " << counter << endl;
                       cin >> temp;
                       int ints = disable(); //clear interrupts
                       strcpy(buffer,temp);
                       if(ints) enable(); //reenable interrupts
                  } while(!stricmp(buffer,"EXIT"));

                  done_handler();
             }

             POINTS TO NOTE

             In a hardware interrupt handler, you must not:

              - longjmp out of the handler
              - use library functions like printf() that may invoke
                DOS calls

             When chaining an interrupt handler with the
             _go32_dpmi_chain... function, don't forget to fill in the
             pm_selector field of the parameter struct with
             _go32_my_cs(), or else you'll have some interesting
             crashes.


             ACKNOWLEDGEMENTS

             Allegro is an excellent graphics/sound/related stuff
             library for DJGPPv2 games programming. It can generally
             be found on x2ftp.oulu.fi, in
             /pub/msdos/programmer/djgpp2/. It's author, Shawn
             Hargreaves, is currently between email addresses, but his
             snail address is:

             Shawn Hargreaves,
             1 Salisbury Road,
             Market Drayton,
             Shropshire,
             England, TF9 1AJ.

             The copyright section, reproduced here since I'm
             including his LOCK_ macros, is:











             "Allegro is swap-ware. You may use, modify, redistribute,
             and generally hack it about in any way you like, but if
             you do you must send me something in exchange. This could
             be a complimentary copy of a game, an addition or
             improvement to Allegro, a bug report, some money (this is
             particularly encouraged if you use Allegro in a
             commercial product), or just a copy of your autoexec.bat
             if you don't have anything better. If you redistribute
             parts of Allegro or make a game using it, it would be
             nice if you mentioned me somewhere in the credits, but if
             you just want to pinch a few routines that is OK too.
             I'll trust you not to rip me off."

             Seeing as I used his code in my tutorial, I'll send him
             my tips on stereo boosting with phase shifts :-)

             Bill Currie came up with the patch to make GCC support
             __attribute__ ((section)) properly, thus making libints
             possible.

             Mark Habersack's ideas about using custom COFF sections
             for loadable resources led me to think of the locked-
             sections scheme used in libints.

             And thanks to everyone on comp.os.msdos.djgpp who
             answered my questions about interrupt handling!

section 1 of uuencode 5.22 of file ints100s.zip    by R.E.M.

begin 644 ints100s.zip
M4$L#!!0``````#>]`R$````````````````(````24Y#3%5$12]02P,$%```
M````-[T#(0````````````````0```!34D,O4$L#!!0``````#B]`R$`````
M```````````$````3$E"+U!+`P04```````XO0,A````````````````"0``
M`$U!3DE&15-4+U!+`P04``````"@O0,A````````````````#````$E.0TQ5
M1$4O4UE3+U!+`P04``(`"``HO`,A)FVDD8\"``#M!```$P```$E.0TQ51$4O
M4UE3+TA724Y4+DAM5,%JW#`0O1O\#Y/D4#ML'-)"(>VE:0K=%EH"3<G1R-9X
M/8VL,9*\[E+Z[QW9NUF7Y+`D&CV]>?/>X#-JK,8&RG+]\.7[?;E.DS,YD\5E
M28K4S+"Z-X./OS3!WP&=A=/;4_ AT C"+2:FHB]/%\KIT?E$,@*Q`U]@%99;="!
MH<HIMXNXB;Y8PP7\H*XWU!#J%3BN!A]6\N!E#K(;*<&GKY_O[M(DN\WAQBA'
M-7PLX(&,(=5YR-14^Z"J44J=+S1V;(N:B^$QAZOKZ[=I\M,CA)8\-.Q@;%7`
MK>C;\0"CLB)`>3`LS>2O,@;T@%`[U!1`WFQHB[9(DV^L)]T@;SVQ]>!;'HR&
M"J%3[E%NY+T?ZA:HD7:X AT SA3X`C0Y(.C:@BHA6JMM AT C-$%G3Y/QR=C)-/`88
M>AB=ZGOI`5D=U;"M,9]A6R8MCE`HMU@'G^7O#T_K5HE3TDO9YTFD2:^\GQT0
M7=!WI4<C#.*&(.*9FR9VYP84E!M^\[K4?4<"VY!M&&9E:5+MEK<;#&7O.`@3
MZK)CC>53[TDA.Y$("^V>N AT C)Y'<0%Z6LEF=N_&(NHZ AT 338;K:/#>FDGUWJV7
M!XYC'O:P53&C()B8Q,PDF4^)CQ1:H=A!AQV['4CD AT 25"]"=I<@,.P^!LM.7B
M2B#*1 AT -5B"Y*M#%>R]`XQ*?05L#Q<B0O]WL):2+&SU1QAI;J=K$[,1LI2W9.
M0E$>)^NR7(!HIR5MX[HX[(VJ4<^SL2-)1IGGLQ>SW=%1<6CFBH<YC]440W:^
M!^=9?O1ZWUYD'VR^D/$"OILW1S/Z>!8E7IAP6B66&?9<)W`OUKSR:2*:'?SB
MZ AT 36TY6?G'HRH-H=E>7%8CO^FW^Q$K/$E[Y+?Y<?H^-_:?(/4$L#!!0``@`(
M``X)`R'"=`()3@,``!\'```2````24Y#3%5$12]365,O3$]#2RY(=55M;]LV
M$/YNP/_AY@*#E,5J^F5`&PR8YV1-N[=B2;=A7P1*/$5L*%(@*:?>L/^^.U)R
M%"<U8%M'/G?/W7-'ZH5JC,0&RO+GW[8_E5?+Q0LRE<'9RG+Q\H1_V2ZN8`V]
MQT%:$8)3U1#00V,=B"'83 AT 15@[;UG3*WH`Q<O'_[X<-RD6USV&CA:/.'`OY4
M6BO1><A$7/M>5/>TU/E"8F=-4=MBN,OAU>O7WRX7'SU":%7BN&]%P!TZV-L!
M[H4)IR`\\1$9_0NM00X(M4.I`I#/K=JA*9:+7ZQ4C4()Y.N5-1Y\:P<MH4+H
MA+NC'?+W0]V":H@.]R`<\5H&2.7'0B6%NA([A&;@J,O%AAAM]0GKX*<X]RJT
M'`%$WSO;.T49`RMW>5&>P$PS+IG#LUHHEPM2JV_W7M5"0T<ZN#V5BR;NEYXH
M..\LY[((HF,R*8,4`7:DI:@TA>X&'SBT,BHHH96?P-YC5^D]U%8B)>!;;A-5
MV7)14 AT 0QQ1KED5AK%H+5-+&J,9'E8E5H<EA1`XQ,\1XC@`#A<UA%XAL+P]1'
MQI["V^WVD&8M>LX;;$.;SI$_I=A27!W3F[,>]$N\O-=-O96?;ON^T.9N5K\/
M#U)=BZXGEL&+6WS#"YS#FA\`"!J AT L1;*\D!1EI!E8^?^V/R>Y_`=G)U'],XJ
M"95PU(XO./SX\==MGI]'FLTUK&'D*29YHGSC8DG,;])VG.6SIV"2<@(3;P+3
MQV%<WHIQ#.:#(CR@<"0E/?36>T4:C^U"%X0RM%5A8]U!*)*;)#9[5@.=&_J0
MVH#.?\4T)R_YEZ^#HQL`XIVA(G,"3;?(@WZ'P<C&T<E/P=C:=ATW]@C/\CUR
MX%'*$W<\:,[SN'#_IXD="S]-N=!*M0<M(?.(#Z.1I^SP,]5GH&Z%`Z\#F=Q'
MWY5EMDKVBGLWA^$1#)^'>1V/T2P:V\]$>PS#!U AT JL7;6<W<'9_ AT 0>/4/QHK'
M:H5#ZJHR5&3`YR6_N?SKIKQ^]_<E9!E/5?[U6,(:1CM5FC_QO-C<;(X]8[HS
M3[;S9UB3VY,4OGD2.SFK)KU\ZEX/GK\'F5;;%?Q+"#12-4F540\/9_%EX(>Z
M1D\-7[^*=B.4'AP"JS$&X5-]="S.O\3[WYQL>OH?4$L#!!0``````#>]`R$`
M```````````````,````4U)#+TQ)0DE.5%,O4$L#!!0``@`(`-.]`R&?2_=T
MG@$``+<"```2````4U)#+TQ)0DE.5%,O3$]#2RY#95)MBQ,Q$/X>V/\P(&CW
MJ%OO!.$X%6M;]'S!`RN*7Y;9S6PW-IL<FZ2UJ/_=27)W]?!+0IZ9YV6&/%"F
MU4$2/)?7 AT ZKZEX4X0N[@9MJVVP078G82SP^?%N^K!3R&:T=!6O1^5$WPY*"S
M(V#P=D"O6HA$93:@#"S?O;FZ*L1D4<)<X\C%UQ5\55HK'!Q,,&&OL-DS-+A*
MTF!-U=HJ;$LX/3]_5H AT OCL#W*GOL>_2THQ$.-L`>C9\".O9C,[Y1:Y"!H!U)
M*@_,V:@=F:H0'ZU4G2()S'7*&@>NMT%+:`@&'+=<8;X+;0^J8SLZ`([L:V.#
M5.YF4,E2;W%'T(6H6HB363R=3V,KX]/H).L#>7@!3R[R[E8<(NIRIA;-H]P5
M74"BQVEZUHY:GY*Q%V^4K>^Z6BMI6 AT B&I$U`0\[##]O<*.84M_9W2I,2?A4"
M(`7K4&F2%_G=38XY2QC)A]'D4F[C[/7&/CVKX\^HDVA,.GGH=$H</\)J62_G
MZWG]^?+[JKQ'_OT_.TX0V9Y^^EOV>O5M_2_[WNI.,Y:C'</_*<1?4$L#!!0`
M`@`(`(R\`R$IV98'D@$``)L#```6````4U)#+TQ)0DE.5%,O5$535$Q/0TLN
M0Z6246O;,!2%WPW^#VK&BIR&)FW?ZF4PO.UEHX5"^Q00CB1;(K)D)#E=&?GO
ME60E-B&A#]6#,.?><_7IR%^XQ*(C%'S#2G)US;ZGR:#5ZN[V2")MPX\D\V;F
M[)5+>T(7"F^"G"9;Q0EHWE#520PS@%!IK>;KSE*$`(1_'XL_OWZBW\\/19;E
MOEV4E AT L*W&!OVY;ZG.?EQU.P(,^+/"$RM.:R4D`)@E AT IB:`?N$%8)SC_IXDK
MS*=$`:,::AF7->@,K3HQG8=:#W=UE<<1H1VSDDM@`P&(!+'?\`:Y2\$1VW7;
M.&1!L55Z=J2KJC+4!L(TV7G"QHT^D,5XHB&8'4T>:CY]-Q=;KJ2!61X-W**M
M$P]*V$;AU=2B5BOK>BA!C2+4\U*MN[9W*@T7_VZ*V>4(-<X:4):..G3WO3'1
M\9&1%2S!8J2^,O?J\&*S9MS";']-#U[!^!]<+/?>4=DMW&H'6L%)H3H/?`^^
MDI5>R<FL-\;#^S6<WA</M5WXV$4<%P9FYY(RGTA*N_<N#>TC&F+SU?#*[U!+
M`P04``(`"`!%O`,AI:J+LS@#```6$@``%0```%-20R],24))3E13+TE.5%=2
M05`N4\V7T6_;-A#&WP7H?S@@"QH+GFRG;89Z+\O2H$F#%&Z<H<`PP*#$DTV$
M%E62LNS_OD=);AQMPQ)+F^<'V3R+/]UWWY&2?.^(8R)2A.G]^<7-;'K]^R6,
MSH+1\/2-[_G>(+ABFA=,(XC4HM9Y9F'!4BY1 AT Q219GKCSKO^=/_E[GP23N%'
MF(IE)D4BD/=!JR AT WMD]3_IHBTCF%X/W'#Y.)[YU<].!<,BUB^#6$+T)*P98&
M3E AT 9^X5%!866)N2X5&D8JS!_Z,'HW;LSW_O-(-B%,)`H#<6"65Q1AAN50\%2
M2H`9D(HN1M],2N`Y0JR1"PLT9RY6F(:^=ZMXF3?07"-4:L`L5"XY1`A+IA_H
M'YIO\G@!(J'+X0:<)JO<"5P8JT646^2$NF(KA"1W5-\+!N[H>Z'!V!(70LF9
MI?%<JDC"3.5ZQHWOU3_&O@<`8:$TAV'EPO7=9R AT TRS)*S-$&P;T36WM'2D AT P
MZ2`<D\85&6:U2[.3E"VQ!S2"F=!?ZW$%F>+CB4X&22)74V0:&.<:C0%5"OWN
M.4DJ)?3!H*T@[N]'6U<D494PNEB_O&RL%8'("YI.AI.$L*K(MO=NSV\N9ZY_
M)I=W=;K/_/S\1UFKEA]'>>K-OI2.<JG[8FOAT9$KRKX49_K+"=TJ,I;%#S,Z
M:EOG,MXOE]!D+-[=K?JOIJ_VR053OF<F%:5ASGCO?@G+?6G8IKJ=KP"[/O`*
MV&G:<:NZ9+E9,`DMJ^LH!1QSTP4%.Z$DG5#FYM!=YU9`M+$(P_4IPB"XF(XA
MTYB(=3!X$66I5L7V5MH_9FMBS='"^^GS.+L4FMW?S^P_4;`32M()96X.[?0@
M2%4!IA`V7D"Y#YOGNMQY+E5=C.D?1^M6%.F6=$96Q^O#5O?1:6-:*OJA>9-T
MG9S]YXKJ72IRB]FX)VHE>=4WH%)(L:@&?]=#.Q1G4KR&@^]U974;SPY46[9^
M$25V[S!!.6T0E(.:^**]KB./5%87=Q!HM!K%KD__F-`CI3(:_A<>E;ETL(Y<
M79Z]=/Y5CUK<[)]2DDXHV`F%MZ>T>#C<4H1&V\:D$N)[3]Z"A[U&8-0,G#8#
MKYN!-\W`VV;@K!GXJ>?>R[\!4$L#!!0``@`(`'"]`R$=X6VIKP```-X!```4
M````4U)#+TQ)0DE.5%,O34%+149)3$5]D#$.PC`,1>=:\AT\,,"0,+!U[@(C
MG"!$5:FH&I1&*L?'$<$U$B525,?Y[\>_J9W2$/S=ML^6:I)CH,VV.37'\VX_
M]->\^S%-UB%4G?=DNH/6FB'?YD^84B03Y!(!00G5"UY9&:_Z&K<!@<I"^#T2
MFQ9W/L_1/;BZS5QGN'*1XDJ6?UAT(^M6?P*"!/H.XVD)420R/D+Q9^I=*:PT
M,O=1*7`9L99Q+PLLK8R+%N$%4$L#!!0``@`(`,^]`R'<URZ92@,```$(```3
M````4U)#+TQ)0DE.5%,O2%=)3E0N0XU5;6_;-A#^+D#_X>:N AT N0ZCIVTZ5HW
M1;MV6#:L0;&NZ(>U$&B1B@^E2(TO=HTB_WTD]>HEP!PCI],=[[F[YTCJ`8J"
M6\K@!:TKG&]>QM%@NI'G9_\QZ;T^W>Q0F&"/H]/I%5%T1Q0#9V1*V=K`A AT C*
MF0*.:T74WJ^[^O3;]5_S-W`"'["J.9;(Z`R47%MM9B[@?@P4-\X$;W__]?W[
M.$K?9/":$X4%_#R'3\@YDDI#2H+M%5GOG*G2<\HJ*>:%G-NO&2R?/;N(HX^:
M@=F AT AE(JV&V(85M7WUY:V!'A"B`:N'3)W)-P#M0R*!2C:,#%W."6B7D<O9,T
MU`TN5J,4&O1&6DYAS:`BZJOSN'AMBPU AT Z=*Q/?B>C/0+*&JC<&T-HP[JBFP9
ME-:CQM'TU,L'E)4H&%Q_?)=?O;Y^^\<O?WZ`G[QG*Y&"QBIW[*3NO^,WUXS/
MQN^RU!E\CR,`R'.BJSQWSZWDQ"!G[B4-KDEM]:;DG\5G,QD,'!X^+/0=XX^"
M?3,NL3:#ZR!H<1=G.3)QQ>Y$]I#/1PN%K(>WYW`Z!2%!6E-;H\$S%,P3-8%T
MU'XV.S1Y!KJE13%Q7D:^3;)5'-UZ)EUBI AT 2,.5O,.FW9:V>]=MYKCWOM2:]=
M]-K3U0$XJG\<L)?+(,^"/`_R<9!/@KP(T@?'D39N3$4(G[:P^N_Q9O@"E_`]
M&>I.AL*3H?)D*#T0<<1?,O28#$TF0Y>]^O1V7&K8E].=(G5]?ZT-#4G#0](0
MD31,'%L;)`UG24-:TK`6'J&8(U$.Z+6:T;O5+F8'O]O5T=!'+VQWB!4:;P2C
M_OI0)I0DK<JI/J*?]C9`@2;?LL+HM#OQ#83K)*_V3DG#IO]_N#CR^35KX,+U
MXA6I9B%1.BVM*+(TZ]*,SH[+=;)<]59LU-Q_-7+_-<DUNT%12M"M*XA2JA0O
M%RM\,1[!"A\]RKKVL4Q_"$/"+]DH&?;\-#G+[MA?7IXL,U#,6,=N5U(0`:5=
MY:?<^J9I?\(Z9^:\OOO,]]NLTCBO*W_+!#Y:9HN&V=XMR]*1UP7WAZ&#O8<3
MD]=*&H?):%Y)RO+^HY<WS*?M`!*-V:B7MK\6N;O2PI24JY%H-HRP6=3-;,2"
M)V&QZEAT"/\"4$L#!!0``@`(``J"`B&T'S],#@$``(`"```-````3$E"+T1*
M1U!0+DQ.2X51P6J#0!0\5_`?'CEI0V6;WB(Y2#2M4-VBFT-[66Q60XO4X.Y!
M"/WWKKMQ8Q-IQ8,S;^:]8<1;\K(E=(.S)"#.;-=4U=V^>5C,7-N*4I*].EP4
MK9`HC]8DQFEN6T?;`O!$V0D`U-TCA.9Y_!;A#7V*@C#*<EB"T@#<.DKG:E1J
MSTJZP0>JX0H\7X]Y/1#@&WLMSO:KN2=!\!P_I@[J%@BYBOY6\5 AT A"O AT U/*=B
MG_O#@58?+1=T)YKVXF1/N6-E74P(QTO8]1(VN61**(.>A%#JU*8@!<<%#<2X
M(,F9 AT B[G?S7TSCF`_G&G%#"'(`RS`2U[V1%N^L><DRYW`&N<)#@UX;^8/BV#
MZ\__0LCW!U!+`P04``(`"`"HO0,A;`US"G,```#4````%0```$U!3DE&15-4
M+TE.5%,Q,#!3+DU&5,O,2\XI34G5+ZXLUL\HS\PKT<O AT Y4(6S,E/S@:)Y60F
MZ:=DI1<4Z.7D9?-RY2;F9::E%I?H`[44&QH8%.OEII5@$RY++>+E*BY*U@<:
M`!*$VI*,*@@DRHL2"_2*487!EJ,IS4W,3DW+S$E%%2T!6 AT I3#0!02P,$"@``
M````[KP#(6W&)=$K````*P```!4```!-04Y)1D535"])3E13,3`P4RY615)!
M;&%R:6,G<R!H87)D=V%R92!I;G1E<G)U<'0@;&EB<F%R>2!6,2XP,`T*4$L!
M`A0`%```````-[T#(0````````````````@``````````0`P`````````$E.
M0TQ51$4O4$L!`A0`%```````-[T#(0````````````````0``````````0`P
M````)@```%-20R]02P$"%``4```````XO0,A````````````````!```````
M```!`#````!(````3$E"+U!+`0(4`!0``````#B]`R$````````````````)
M``````````$`,````&H```!-04Y)1D535"]02P$"%``4``````"@O0,A````
M````````````#``````````!`#````"1````24Y#3%5$12]365,O4$L!`A0`
M%``"``@`*+P#(29MI)&/`@``[00``!,``````````0`@````NP```$E.0TQ5
M1$4O4UE3+TA724Y4+DA02P$"%``4``(`"``."0,APG0""4X#```?!P``$@``
M```````!`"````![`P``24Y#3%5$12]365,O3$]#2RY(4$L!`A0`%```````
M-[T#(0````````````````P``````````0`P````^08``%-20R],24))3E13
M+U!+`0(4`!0``@`(`-.]`R&?2_=TG@$``+<"```2``````````$`(````",'
M``!34D,O3$E"24Y44R],3T-++D-02P$"%``4``(`"`",O`,A*=F6!Y(!``";
M`P``%@`````````!`"````#Q"```4U)#+TQ)0DE.5%,O5$535$Q/0TLN0U!+
M`0(4`!0``@`(`$6\`R&EJHNS.`,``!82```5``````````$`(````+<*``!3
M4D,O3$E"24Y44R])3E174D%0+E-02P$"%``4``(`"`!PO0,A'>%MJ:\```#>
M`0``%``````````!`"`````B#@``4U)#+TQ)0DE.5%,O34%+149)3$502P$"
M%``4``(`"`#/O0,AW-<NF4H#```!"```$P`````````!`"`````##P``4U)#
M+TQ)0DE.5%,O2%=)3E0N0U!+`0(4`!0``@`(``J"`B&T'S],#@$``(`"```-
M``````````$`(````'X2``!,24(O1$I'4%`N3$Y+4$L!`A0`%``"``@`J+T#
M(6P-<PIS````U````!4``````````0`@````MQ,``$U!3DE&15-4+TE.5%,Q
M,#!3+DU&5%!+`0(4``H``````.Z\`R%MQB71*P```"L````5``````````$`
M(````%T4``!-04Y)1D535"])3E13,3`P4RY615)02P4&`````!``$`#1`P``
&NQ0`````
`
end
sum -r/size 50958/8719 section (from "begin" to "end")
sum -r/size 26808/6306 entire input file


--------------07F6BF90C0A33425CE25AEB4
Content-Type: text/plain; charset=us-ascii;
 name="djints.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="djints.txt"





             The Dark Art of writing DJGPP Hardware Interrupt Handlers

                        (Which might conceivably be useful
                            for other DPMI environments)

                                    Version 1.0

             (C) Alaric B. Williams (alaric AT abwillms DOT demon DOT co DOT uk)
             Copy in unmodified form, as long as you give all due
             credit.

             PREFACE

             This tutorial assumes an understanding of what a hardware
             interrupt is for, and how it works, along with the usual
             programming skills. Ideally, you will have coded a
             hardware interrupt handler in real mode DOS before.

             No guarantee of the correctness of information in this
             tutorial is given; if any errors are found, please report
             them to alaric AT abwillms DOT demon DOT co DOT uk, and I will fix them
             for the next release. I cannot accept responsibility for
             any damages caused as a result, direct or indirect, of
             using the information presented herein.

             Anyway, onto the interesting parts!

             INTRODUCTION

             Hardware interrupt handling under DPMI is not all that
             different to the same under real mode. There are just a
             few more things to consider.

             All interrupts in DPMI protected mode go through the DPMI
             host, first of all. The DPMI host tells the CPU to stop
             using the normal interrupt vector table at real mode
             address 0000:0000, by setting the 386+ IDT (Interrupt
             Descriptor Table) register to a vector table somewhere
             else in memory.

             The DPMI host also maintains a 'virtual interrupt vector
             table', which lists the addresses of protected mode
             handlers. When an interrupt comes in, the DPMI host scans
             it's internal table; if that interrupt number is claimed
             by a protected mode function, it switches to protected
             mode if the CPU is in real mode (for example, when you
             use getch(), the CPU is running the real mode getch()
             routine in the BIOS), and simulates the interrupt. If the
             interrupt is not claimed by protmode, it uses the
             interrupt vector table in DOS memory to perform the jump
             to a real mode handler, after first making sure the CPU
             is in real mode.

             For example, if our protected mode application is working
             away, and the timer tick interrupt goes off (IRQ 0, INT
             8):












             1) CPU stores protected mode state and looks up the
             address to jump to in the IDT.

             2) As expected, it jumps to the DPMI host's interrupt
             wrapper for INT8.

             3) Wrapper checks to see if its interrupt has been
             claimed by a protected mode routine.

             4) It hasn't, so the wrapper switches to real mode, and
             jumps to the address stored in interrupt vector table
             entry 8, thus executing the real BIOS interrupt handler.

             5) Upon return from the BIOS handler, the wrapper cleans
             up after itself, returning to protected mode and
             restoring the machine state so our protected mode
             application continues to run smoothly.

             Evidently, a lot of switches between protected mode and
             real mode can be incurred; the DPMI host keeps the
             workings of this hidden from us (thankfully!), but the
             performance hit can be significant. Think twice about
             installing a timer tick interrupt at 20Khz!

             <RANT>

             While we're on the topic, I'd like to dispel a myth.
             People are often heard moaning "Oh! Protmode's so slow!
             All that switching about, just to call a DOS interrupt,
             or whenever the user presses a key and invokes IRQ1, the
             keyboard interrupt! I'll stick to real mode programming,
             thankyou!".

             Well, if you hear them saying that, snigger quietly, and
             let them write their 'fast' real mode games, because
             yours will quite likely run faster. Think about it: most
             PCs do not run in real mode at all these days; it's all
             V86 mode, if they're running any kind of 386 memory
             manager, DPMI host, or Windows product; every time an
             interrupt occurs, their DPMI/VCPI/whatever host takes a
             slice of time to see if any protected mode code wants
             that interrupt. And you get the advantages of large flat
             virtual memory spaces, coupled with more control about
             where your interrupts go. A DPMI application that's
             really hungry for speed can install a protected mode
             interrupt, or place a real mode routine in DOS memory
             hooked to the real mode handler, depending on where their
             program spends the most time. If it's a DOS I/O bound
             application, the real mode handler will run faster, as it
             does not incur a mode switch; and if it's a compute bound
             application, sitting in protected mode, you can write a
             true protected mode handler, which will execute sooner
             than a real mode one.

             </RANT>

             LOCKING











             The second important thing to consider with DPMI
             interrupt handlers is the vexed issue of paging. DPMI
             memory (in which your DJGPP application executes) can be
             paged out onto disk, making 'virtual memory' possible.
             Normally, if any 'paged out' RAM is requested, the DPMI
             host can bring it back in invisibly; but this cannot be
             done during the execution of an interrupt handler. If an
             interrupt handler, any other functions it invokes, or any
             variables it touches, is 'paged out', your code will bomb
             out with a page fault. The solution is to 'lock' the
             memory regions that must be available, telling the DPMI
             host to keep them in active memory at all times. This can
             be done with the DJGPP library functions
             _go32_dpmi_lock_code(void *,unsigned long) and
             _go32_dpmi_lock_data(void *,unsigned long), which each
             take a pointer and a length, specifying the block of
             memory to be locked.

             Before installing your handler, lock all code and data it
             touches. To lock a static variable, use:

             _go32_dpmi_lock_data(&my_var, sizeof(my_var));

             To lock a dynamically allocated memory block, you must
             know its size:

             char *buffer = malloc(buflen);
             assert(buffer);
             _go32_dpmi_lock_data(buffer,buflen);
             _go32_dpmi_lock_data(&buffer,sizeof(buffer));

             Don't forget to lock the pointer variable, as well as the
             data!

             Locking code is slightly different; we can't sizeof() a
             function, so we have to use a trick:

                  void my_handler()
                  {
                  }

                  void lock_my_handler()
                  {
                    _go32_dpmi_lock_code(my_handler, (unsigned
             long)(lock_my_handler - my_handler));
                  }

             (Copied directly from the info page). In other words,
             declare a function after your handler - hopefully a
             dedicated dummy function, in case you forget later on and
             move the function you're using as a marker - and use
             pointer arithmetic to deduce the code size of your
             handler function, while praying that gcc puts the
             functions in the order you specify!

             The Allegro games programming library uses a set of
             convenient macros to automate this. They are:











             #define END_OF_FUNCTION(x) void x##_end() { }

             #define LOCK_VARIABLE(x)  \
                 _go32_dpmi_lock_data((void*)&x, sizeof(x))

             #define LOCK_FUNCTION(x)  \
                 _go32_dpmi_lock_code(x, (long)x##_end - (long)x)

             LOCK_VARIABLE is pretty self explanatory; END_OF_FUNCTION
             is used like so:

             void my_function() {
                ...
             }

             END_OF_FUNCTION(my_function)

             and creates a dummy function named my_function_end.

             LOCK_FUNCTION uses this to expand:

             LOCK_FUNCTION(my_function)

             into:

             _go32_dpmi_lock_code(my_function, (long)my_function_end \
                    - (long)my_function);

             thus taking care of the paperwork for you.

             Perhaps the easiest and safest method of locking I have
             seen is to tell GCC to put everything you want locked
             into a special section of the COFF executable, which can
             then be locked all in one go. This is accomplished using
             the GCC "__attribute__ ((section "name"))" feature;
             however, in order to make this work (with the current
             version of DJGPP, 2.00), you have to download the GCC
             source code and fix a little bug. This fix is the
             brainchild of Bill Currie, and consists of adding the
             lines:

             #define ASM_OUTPUT_SECTION_NAME(FILE,DECL,NAME)\
             do { \
                fprintf(FILE,"\t.section %s\n",NAME); \
             } while(0)

             to the file "configure/i386/go32.h" in the gcc source,
             then recompiling.

             You also need to modify /lib/djgpp.lnk to tell the linker
             where to put these special 'locked' sections. I have
             written a library to do all this for you - "libints". It
             should be included with this document. See the sample
             code (in the directory /src/libints) for instructions.

             THE ACTUAL ACT OF INTERRUPT CLAIMING












             This is pleasantly painless. As GCC does not support an
             'interrupt' modifier on function names to tell them to
             provide the correct wrapper (with an IRET on the end),
             you must use a wrapper; but the DJGPP libc.a can do this
             for you in one fell swoop.

             If you merely want to chain onto a vector, and still have
             it passed back to the previous handler (which may be the
             original real mode one), simply use the following code:

             #define TIMER_INT 8

             void my_int_handler() {
               ...
             }

             ...

             _go32_dpmi_seginfo my_handler;
             my_handler.pm_offset = (int)my_int_handler;
             my_handler.pm_selector = _go32_my_cs();
             _go32_dpmi_chain_protected_mode_interrupt_vector( \
                       TIMER_INT, &my_handler);

             If you want to totally take an interrupt over, it gets a
             little more complex. Say we were installing the same
             handler - we would use:

             _go32_dpmi_seginfo my_handler;
             my_handler.pm_offset = my_int_handler;
             _go32_dpmi_allocate_iret_wrapper(&my_handler);
             _go32_dpmi_set_protected_mode_interrupt_handler( \
                         TIMER_INT, &my_handler);


             Since we've allocated a wrapper, we must, at some point:

             _go32_dpmi_free_iret_wrapper(&my_handler);

             Just make sure you've removed your interrupt handler
             before doing that!

             Please note: The wrapper DJGPP provides is not locked in
             memory, and is as such unreliable. Included in libints is
             a proper locked iret-wrapper system which is a little
             easier to use than the standard _go32_dpmi functions, and
             includes a useful function for simulating a protected
             mode interrupt, which makes it easy to chain to the
             original handler. This is more flexible than the GO32
             chaining  function used above, as you have the option of
             not chaining (useful for timer interrupts), and can do
             things like executing some code before chaining and some
             after. See the libints sample source for further
             instructions.

             If we want to preserve an interrupt vector, in order to
             restore it to its original value (which is kinda good
             practice!), we can simply use the code:











             _go32_dpmi_seginfo old_handler;
             _go32_get_protected_mode_interrupt_handler(TIMER_INT, \
                  &old_handler);

             ...

             _go32_set_protected_mode_interrupt_handler(TIMER_INT, \
                  &old_handler);

             EXAMPLE

             #include <go32.h>
             #include <dpmi.h>
             #include <sys/farptr.h>
             #include <string.h>
             #include <iostream.h>
             #include <dos.h>

             _go32_dpmi_seginfo old_handler,my_callback;

             volatile int counter;
             char *string;

             void my_int() {
                  counter++;
                  char *ch = string;
                  int addr = 0xA0000;
                  while(*ch) {
                       _farpokeb(_dos_ds,addr,*ch);
                       ch++;
                       addr += 2;
                  }
             }

             void end_int() {}

             void init_handler() {
                  _go32_dpmi_lock_data(&counter, sizeof(counter));
                  _go32_dpmi_lock_data(&string, sizeof(string));
                  _go32_dpmi_lock_code(my_int, (long)end_int - (long)
                                       my_int);
                  _go32_dpmi_get_protected_mode_interrupt_handler(8,
                                      &old_handler);
                  my_callback.pm_offset = (int)my_int;
                  _go32_dpmi_allocate_iret_wrapper(&my_int);
                  _go32_dpmi_set_protected_mode_interrupt_handler(8,
                                      &my_int);
             }

             void done_handler() {
                  _go32_dpmi_set_protected_mode_interrupt_handler(8,
                                      &old_handler);
                  _go32_dpmi_free_iret_wrapper(&my_int);
             }

             void update_text(char *s) {
                  int ints = disable();










                  string = s;
                  if(ints) enable();
             }

             main() {
                  counter = 0;
                  update_text("");
                  init_handler();
                  
                  char buffer[80],temp[80];
                  _go32_lock_data(buffer,80);

                  cout << "Type some text followed by a CR,";
                  cout << " or EXIT to quit" << endl;
                  do {
                       cout << "Counter = " << counter << endl;
                       cin >> temp;
                       int ints = disable(); //clear interrupts
                       strcpy(buffer,temp);
                       if(ints) enable(); //reenable interrupts
                  } while(!stricmp(buffer,"EXIT"));

                  done_handler();
             }

             POINTS TO NOTE

             In a hardware interrupt handler, you must not:

              - longjmp out of the handler
              - use library functions like printf() that may invoke
                DOS calls

             When chaining an interrupt handler with the
             _go32_dpmi_chain... function, don't forget to fill in the
             pm_selector field of the parameter struct with
             _go32_my_cs(), or else you'll have some interesting
             crashes.


             ACKNOWLEDGEMENTS

             Allegro is an excellent graphics/sound/related stuff
             library for DJGPPv2 games programming. It can generally
             be found on x2ftp.oulu.fi, in
             /pub/msdos/programmer/djgpp2/. It's author, Shawn
             Hargreaves, is currently between email addresses, but his
             snail address is:

             Shawn Hargreaves,
             1 Salisbury Road,
             Market Drayton,
             Shropshire,
             England, TF9 1AJ.

             The copyright section, reproduced here since I'm
             including his LOCK_ macros, is:











             "Allegro is swap-ware. You may use, modify, redistribute,
             and generally hack it about in any way you like, but if
             you do you must send me something in exchange. This could
             be a complimentary copy of a game, an addition or
             improvement to Allegro, a bug report, some money (this is
             particularly encouraged if you use Allegro in a
             commercial product), or just a copy of your autoexec.bat
             if you don't have anything better. If you redistribute
             parts of Allegro or make a game using it, it would be
             nice if you mentioned me somewhere in the credits, but if
             you just want to pinch a few routines that is OK too.
             I'll trust you not to rip me off."

             Seeing as I used his code in my tutorial, I'll send him
             my tips on stereo boosting with phase shifts :-)

             Bill Currie came up with the patch to make GCC support
             __attribute__ ((section)) properly, thus making libints
             possible.

             Mark Habersack's ideas about using custom COFF sections
             for loadable resources led me to think of the locked-
             sections scheme used in libints.

             And thanks to everyone on comp.os.msdos.djgpp who
             answered my questions about interrupt handling!

section 1 of uuencode 5.22 of file ints100s.zip    by R.E.M.

begin 644 ints100s.zip
M4$L#!!0``````#>]`R$````````````````(````24Y#3%5$12]02P,$%```
M````-[T#(0````````````````0```!34D,O4$L#!!0``````#B]`R$`````
M```````````$````3$E"+U!+`P04```````XO0,A````````````````"0``
M`$U!3DE&15-4+U!+`P04``````"@O0,A````````````````#````$E.0TQ5
M1$4O4UE3+U!+`P04``(`"``HO`,A)FVDD8\"``#M!```$P```$E.0TQ51$4O
M4UE3+TA724Y4+DAM5,%JW#`0O1O\#Y/D4#ML'-)"(>VE:0K=%EH"3<G1R-9X
M/8VL,9*\[E+Z[QW9NUF7Y+`D&CV]>?/>X#-JK,8&RG+]\.7[?;E.DS,YD\5E
M28K4S+"Z-X./OS3!WP&=A=/;4_ AT C"+2:FHB]/%\KIT?E$,@*Q`U]@%99;="!
MH<HIMXNXB;Y8PP7\H*XWU!#J%3BN!A]6\N!E#K(;*<&GKY_O[M(DN\WAQBA'
M-7PLX(&,(=5YR-14^Z"J44J=+S1V;(N:B^$QAZOKZ[=I\M,CA)8\-.Q@;%7`
MK>C;\0"CLB)`>3`LS>2O,@;T@%`[U!1`WFQHB[9(DV^L)]T@;SVQ]>!;'HR&
M"J%3[E%NY+T?ZA:HD7:X AT SA3X`C0Y(.C:@BHA6JMM AT C-$%G3Y/QR=C)-/`88
M>AB=ZGOI`5D=U;"M,9]A6R8MCE`HMU@'G^7O#T_K5HE3TDO9YTFD2:^\GQT0
M7=!WI4<C#.*&(.*9FR9VYP84E!M^\[K4?4<"VY!M&&9E:5+MEK<;#&7O.`@3
MZK)CC>53[TDA.Y$("^V>N AT C)Y'<0%Z6LEF=N_&(NHZ AT 338;K:/#>FDGUWJV7
M!XYC'O:P53&C()B8Q,PDF4^)CQ1:H=A!AQV['4CD AT 25"]"=I<@,.P^!LM.7B
M2B#*1 AT -5B"Y*M#%>R]`XQ*?05L#Q<B0O]WL):2+&SU1QAI;J=K$[,1LI2W9.
M0E$>)^NR7(!HIR5MX[HX[(VJ4<^SL2-)1IGGLQ>SW=%1<6CFBH<YC]440W:^
M!^=9?O1ZWUYD'VR^D/$"OILW1S/Z>!8E7IAP6B66&?9<)W`OUKSR:2*:'?SB
MZ AT 36TY6?G'HRH-H=E>7%8CO^FW^Q$K/$E[Y+?Y<?H^-_:?(/4$L#!!0``@`(
M``X)`R'"=`()3@,``!\'```2````24Y#3%5$12]365,O3$]#2RY(=55M;]LV
M$/YNP/_AY@*#E,5J^F5`&PR8YV1-N[=B2;=A7P1*/$5L*%(@*:?>L/^^.U)R
M%"<U8%M'/G?/W7-'ZH5JC,0&RO+GW[8_E5?+Q0LRE<'9RG+Q\H1_V2ZN8`V]
MQT%:$8)3U1#00V,=B"'83 AT 15@[;UG3*WH`Q<O'_[X<-RD6USV&CA:/.'`OY4
M6BO1><A$7/M>5/>TU/E"8F=-4=MBN,OAU>O7WRX7'SU":%7BN&]%P!TZV-L!
M[H4)IR`\\1$9_0NM00X(M4.I`I#/K=JA*9:+7ZQ4C4()Y.N5-1Y\:P<MH4+H
MA+NC'?+W0]V":H@.]R`<\5H&2.7'0B6%NA([A&;@J,O%AAAM]0GKX*<X]RJT
M'`%$WSO;.T49`RMW>5&>P$PS+IG#LUHHEPM2JV_W7M5"0T<ZN#V5BR;NEYXH
M..\LY[((HF,R*8,4`7:DI:@TA>X&'SBT,BHHH96?P-YC5^D]U%8B)>!;;A-5
MV7)14 AT 0QQ1KED5AK%H+5-+&J,9'E8E5H<EA1`XQ,\1XC@`#A<UA%XAL+P]1'
MQI["V^WVD&8M>LX;;$.;SI$_I=A27!W3F[,>]$N\O-=-O96?;ON^T.9N5K\/
M#U)=BZXGEL&+6WS#"YS#FA\`"!J AT L1;*\D!1EI!E8^?^V/R>Y_`=G)U'],XJ
M"95PU(XO./SX\==MGI]'FLTUK&'D*29YHGSC8DG,;])VG.6SIV"2<@(3;P+3
MQV%<WHIQ#.:#(CR@<"0E/?36>T4:C^U"%X0RM%5A8]U!*)*;)#9[5@.=&_J0
MVH#.?\4T)R_YEZ^#HQL`XIVA(G,"3;?(@WZ'P<C&T<E/P=C:=ATW]@C/\CUR
MX%'*$W<\:,[SN'#_IXD="S]-N=!*M0<M(?.(#Z.1I^SP,]5GH&Z%`Z\#F=Q'
MWY5EMDKVBGLWA^$1#)^'>1V/T2P:V\]$>PS#!U AT JL7;6<W<'9_ AT 0>/4/QHK'
M:H5#ZJHR5&3`YR6_N?SKIKQ^]_<E9!E/5?[U6,(:1CM5FC_QO-C<;(X]8[HS
M3[;S9UB3VY,4OGD2.SFK)KU\ZEX/GK\'F5;;%?Q+"#12-4F540\/9_%EX(>Z
M1D\-7[^*=B.4'AP"JS$&X5-]="S.O\3[WYQL>OH?4$L#!!0``````#>]`R$`
M```````````````,````4U)#+TQ)0DE.5%,O4$L#!!0``@`(`-.]`R&?2_=T
MG@$``+<"```2````4U)#+TQ)0DE.5%,O3$]#2RY#95)MBQ,Q$/X>V/\P(&CW
MJ%OO!.$X%6M;]'S!`RN*7Y;9S6PW-IL<FZ2UJ/_=27)W]?!+0IZ9YV6&/%"F
MU4$2/)?7 AT ZKZEX4X0N[@9MJVVP078G82SP^?%N^K!3R&:T=!6O1^5$WPY*"S
M(V#P=D"O6HA$93:@#"S?O;FZ*L1D4<)<X\C%UQ5\55HK'!Q,,&&OL-DS-+A*
MTF!-U=HJ;$LX/3]_5H AT OCL#W*GOL>_2THQ$.-L`>C9\".O9C,[Y1:Y"!H!U)
M*@_,V:@=F:H0'ZU4G2()S'7*&@>NMT%+:`@&'+=<8;X+;0^J8SLZ`([L:V.#
M5.YF4,E2;W%'T(6H6HB363R=3V,KX]/H).L#>7@!3R[R[E8<(NIRIA;-H]P5
M74"BQVEZUHY:GY*Q%V^4K>^Z6BMI6 AT B&I$U`0\[##]O<*.84M_9W2I,2?A4"
M(`7K4&F2%_G=38XY2QC)A]'D4F[C[/7&/CVKX\^HDVA,.GGH=$H</\)J62_G
MZWG]^?+[JKQ'_OT_.TX0V9Y^^EOV>O5M_2_[WNI.,Y:C'</_*<1?4$L#!!0`
M`@`(`(R\`R$IV98'D@$``)L#```6````4U)#+TQ)0DE.5%,O5$535$Q/0TLN
M0Z6246O;,!2%WPW^#VK&BIR&)FW?ZF4PO.UEHX5"^Q00CB1;(K)D)#E=&?GO
ME60E-B&A#]6#,.?><_7IR%^XQ*(C%'S#2G)US;ZGR:#5ZN[V2")MPX\D\V;F
M[)5+>T(7"F^"G"9;Q0EHWE#520PS@%!IK>;KSE*$`(1_'XL_OWZBW\\/19;E
MOEV4E AT L*W&!OVY;ZG.?EQU.P(,^+/"$RM.:R4D`)@E AT IB:`?N$%8)SC_IXDK
MS*=$`:,::AF7->@,K3HQG8=:#W=UE<<1H1VSDDM@`P&(!+'?\`:Y2\$1VW7;
M.&1!L55Z=J2KJC+4!L(TV7G"QHT^D,5XHB&8'4T>:CY]-Q=;KJ2!61X-W**M
M$P]*V$;AU=2B5BOK>BA!C2+4\U*MN[9W*@T7_VZ*V>4(-<X:4):..G3WO3'1
M\9&1%2S!8J2^,O?J\&*S9MS";']-#U[!^!]<+/?>4=DMW&H'6L%)H3H/?`^^
MDI5>R<FL-\;#^S6<WA</M5WXV$4<%P9FYY(RGTA*N_<N#>TC&F+SU?#*[U!+
M`P04``(`"`!%O`,AI:J+LS@#```6$@``%0```%-20R],24))3E13+TE.5%=2
M05`N4\V7T6_;-A#&WP7H?S@@"QH+GFRG;89Z+\O2H$F#%&Z<H<`PP*#$DTV$
M%E62LNS_OD=);AQMPQ)+F^<'V3R+/]UWWY&2?.^(8R)2A.G]^<7-;'K]^R6,
MSH+1\/2-[_G>(+ABFA=,(XC4HM9Y9F'!4BY1 AT Q219GKCSKO^=/_E[GP23N%'
MF(IE)D4BD/=!JR AT WMD]3_IHBTCF%X/W'#Y.)[YU<].!<,BUB^#6$+T)*P98&
M3E AT 9^X5%!866)N2X5&D8JS!_Z,'HW;LSW_O-(-B%,)`H#<6"65Q1AAN50\%2
M2H`9D(HN1M],2N`Y0JR1"PLT9RY6F(:^=ZMXF3?07"-4:L`L5"XY1`A+IA_H
M'YIO\G@!(J'+X0:<)JO<"5P8JT646^2$NF(KA"1W5-\+!N[H>Z'!V!(70LF9
MI?%<JDC"3.5ZQHWOU3_&O@<`8:$TAV'EPO7=9R AT TRS)*S-$&P;T36WM'2D AT P
MZ2`<D\85&6:U2[.3E"VQ!S2"F=!?ZW$%F>+CB4X&22)74V0:&.<:C0%5"OWN
M.4DJ)?3!H*T@[N]'6U<D494PNEB_O&RL%8'("YI.AI.$L*K(MO=NSV\N9ZY_
M)I=W=;K/_/S\1UFKEA]'>>K-OI2.<JG[8FOAT9$KRKX49_K+"=TJ,I;%#S,Z
M:EOG,MXOE]!D+-[=K?JOIJ_VR053OF<F%:5ASGCO?@G+?6G8IKJ=KP"[/O`*
MV&G:<:NZ9+E9,`DMJ^LH!1QSTP4%.Z$DG5#FYM!=YU9`M+$(P_4IPB"XF(XA
MTYB(=3!X$66I5L7V5MH_9FMBS='"^^GS.+L4FMW?S^P_4;`32M()96X.[?0@
M2%4!IA`V7D"Y#YOGNMQY+E5=C.D?1^M6%.F6=$96Q^O#5O?1:6-:*OJA>9-T
MG9S]YXKJ72IRB]FX)VHE>=4WH%)(L:@&?]=#.Q1G4KR&@^]U974;SPY46[9^
M$25V[S!!.6T0E(.:^**]KB./5%87=Q!HM!K%KD__F-`CI3(:_A<>E;ETL(Y<
M79Z]=/Y5CUK<[)]2DDXHV`F%MZ>T>#C<4H1&V\:D$N)[3]Z"A[U&8-0,G#8#
MKYN!-\W`VV;@K!GXJ>?>R[\!4$L#!!0``@`(`'"]`R$=X6VIKP```-X!```4
M````4U)#+TQ)0DE.5%,O34%+149)3$5]D#$.PC`,1>=:\AT\,,"0,+!U[@(C
MG"!$5:FH&I1&*L?'$<$U$B525,?Y[\>_J9W2$/S=ML^6:I)CH,VV.37'\VX_
M]->\^S%-UB%4G?=DNH/6FB'?YD^84B03Y!(!00G5"UY9&:_Z&K<!@<I"^#T2
MFQ9W/L_1/;BZS5QGN'*1XDJ6?UAT(^M6?P*"!/H.XVD)420R/D+Q9^I=*:PT
M,O=1*7`9L99Q+PLLK8R+%N$%4$L#!!0``@`(`,^]`R'<URZ92@,```$(```3
M````4U)#+TQ)0DE.5%,O2%=)3E0N0XU5;6_;-A#^+D#_X>:N AT N0ZCIVTZ5HW
M1;MV6#:L0;&NZ(>U$&B1B@^E2(TO=HTB_WTD]>HEP!PCI],=[[F[YTCJ`8J"
M6\K@!:TKG&]>QM%@NI'G9_\QZ;T^W>Q0F&"/H]/I%5%T1Q0#9V1*V=K`A AT C*
MF0*.:T74WJ^[^O3;]5_S-W`"'["J.9;(Z`R47%MM9B[@?@P4-\X$;W__]?W[
M.$K?9/":$X4%_#R'3\@YDDI#2H+M%5GOG*G2<\HJ*>:%G-NO&2R?/;N(HX^:
M@=F AT AE(JV&V(85M7WUY:V!'A"B`:N'3)W)-P#M0R*!2C:,#%W."6B7D<O9,T
MU`TN5J,4&O1&6DYAS:`BZJOSN'AMBPU AT Z=*Q/?B>C/0+*&JC<&T-HP[JBFP9
ME-:CQM'TU,L'E)4H&%Q_?)=?O;Y^^\<O?WZ`G[QG*Y&"QBIW[*3NO^,WUXS/
MQN^RU!E\CR,`R'.BJSQWSZWDQ"!G[B4-KDEM]:;DG\5G,QD,'!X^+/0=XX^"
M?3,NL3:#ZR!H<1=G.3)QQ>Y$]I#/1PN%K(>WYW`Z!2%!6E-;H\$S%,P3-8%T
MU'XV.S1Y!KJE13%Q7D:^3;)5'-UZ)EUBI AT 2,.5O,.FW9:V>]=MYKCWOM2:]=
M]-K3U0$XJG\<L)?+(,^"/`_R<9!/@KP(T@?'D39N3$4(G[:P^N_Q9O@"E_`]
M&>I.AL*3H?)D*#T0<<1?,O28#$TF0Y>]^O1V7&K8E].=(G5]?ZT-#4G#0](0
MD31,'%L;)`UG24-:TK`6'J&8(U$.Z+6:T;O5+F8'O]O5T=!'+VQWB!4:;P2C
M_OI0)I0DK<JI/J*?]C9`@2;?LL+HM#OQ#83K)*_V3DG#IO]_N#CR^35KX,+U
MXA6I9B%1.BVM*+(TZ]*,SH[+=;)<]59LU-Q_-7+_-<DUNT%12M"M*XA2JA0O
M%RM\,1[!"A\]RKKVL4Q_"$/"+]DH&?;\-#G+[MA?7IXL,U#,6,=N5U(0`:5=
MY:?<^J9I?\(Z9^:\OOO,]]NLTCBO*W_+!#Y:9HN&V=XMR]*1UP7WAZ&#O8<3
MD]=*&H?):%Y)RO+^HY<WS*?M`!*-V:B7MK\6N;O2PI24JY%H-HRP6=3-;,2"
M)V&QZEAT"/\"4$L#!!0``@`(``J"`B&T'S],#@$``(`"```-````3$E"+T1*
M1U!0+DQ.2X51P6J#0!0\5_`?'CEI0V6;WB(Y2#2M4-VBFT-[66Q60XO4X.Y!
M"/WWKKMQ8Q-IQ8,S;^:]8<1;\K(E=(.S)"#.;-=4U=V^>5C,7-N*4I*].EP4
MK9`HC]8DQFEN6T?;`O!$V0D`U-TCA.9Y_!;A#7V*@C#*<EB"T@#<.DKG:E1J
MSTJZP0>JX0H\7X]Y/1#@&WLMSO:KN2=!\!P_I@[J%@BYBOY6\5 AT A"O AT U/*=B
MG_O#@58?+1=T)YKVXF1/N6-E74P(QTO8]1(VN61**(.>A%#JU*8@!<<%#<2X
M(,F9 AT B[G?S7TSCF`_G&G%#"'(`RS`2U[V1%N^L><DRYW`&N<)#@UX;^8/BV#
MZ\__0LCW!U!+`P04``(`"`"HO0,A;`US"G,```#4````%0```$U!3DE&15-4
M+TE.5%,Q,#!3+DU&5,O,2\XI34G5+ZXLUL\HS\PKT<O AT Y4(6S,E/S@:)Y60F
MZ:=DI1<4Z.7D9?-RY2;F9::E%I?H`[44&QH8%.OEII5@$RY++>+E*BY*U@<:
M`!*$VI*,*@@DRHL2"_2*487!EJ,IS4W,3DW+S$E%%2T!6 AT I3#0!02P,$"@``
M````[KP#(6W&)=$K````*P```!4```!-04Y)1D535"])3E13,3`P4RY615)!
M;&%R:6,G<R!H87)D=V%R92!I;G1E<G)U<'0@;&EB<F%R>2!6,2XP,`T*4$L!
M`A0`%```````-[T#(0````````````````@``````````0`P`````````$E.
M0TQ51$4O4$L!`A0`%```````-[T#(0````````````````0``````````0`P
M````)@```%-20R]02P$"%``4```````XO0,A````````````````!```````
M```!`#````!(````3$E"+U!+`0(4`!0``````#B]`R$````````````````)
M``````````$`,````&H```!-04Y)1D535"]02P$"%``4``````"@O0,A````
M````````````#``````````!`#````"1````24Y#3%5$12]365,O4$L!`A0`
M%``"``@`*+P#(29MI)&/`@``[00``!,``````````0`@````NP```$E.0TQ5
M1$4O4UE3+TA724Y4+DA02P$"%``4``(`"``."0,APG0""4X#```?!P``$@``
M```````!`"````![`P``24Y#3%5$12]365,O3$]#2RY(4$L!`A0`%```````
M-[T#(0````````````````P``````````0`P````^08``%-20R],24))3E13
M+U!+`0(4`!0``@`(`-.]`R&?2_=TG@$``+<"```2``````````$`(````",'
M``!34D,O3$E"24Y44R],3T-++D-02P$"%``4``(`"`",O`,A*=F6!Y(!``";
M`P``%@`````````!`"````#Q"```4U)#+TQ)0DE.5%,O5$535$Q/0TLN0U!+
M`0(4`!0``@`(`$6\`R&EJHNS.`,``!82```5``````````$`(````+<*``!3
M4D,O3$E"24Y44R])3E174D%0+E-02P$"%``4``(`"`!PO0,A'>%MJ:\```#>
M`0``%``````````!`"`````B#@``4U)#+TQ)0DE.5%,O34%+149)3$502P$"
M%``4``(`"`#/O0,AW-<NF4H#```!"```$P`````````!`"`````##P``4U)#
M+TQ)0DE.5%,O2%=)3E0N0U!+`0(4`!0``@`(``J"`B&T'S],#@$``(`"```-
M``````````$`(````'X2``!,24(O1$I'4%`N3$Y+4$L!`A0`%``"``@`J+T#
M(6P-<PIS````U````!4``````````0`@````MQ,``$U!3DE&15-4+TE.5%,Q
M,#!3+DU&5%!+`0(4``H``````.Z\`R%MQB71*P```"L````5``````````$`
M(````%T4``!-04Y)1D535"])3E13,3`P4RY615)02P4&`````!``$`#1`P``
&NQ0`````
`
end
sum -r/size 50958/8719 section (from "begin" to "end")
sum -r/size 26808/6306 entire input file

--------------07F6BF90C0A33425CE25AEB4--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019