summaryrefslogtreecommitdiff
path: root/etc/etc.i386/INSTALL.os2br
blob: 71ba1a983c8917d61362c746365c452aab9e745c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
                How It Works -- OS2 Boot Sector

                           Version 1a

             by Hale Landis (landis@sugs.tware.com)


THE "HOW IT WORKS" SERIES

This is one of several How It Works documents.  The series
currently includes the following:

* How It Works -- CHS Translation
* How It Works -- Master Boot Record
* How It Works -- DOS Floppy Boot Sector
* How It Works -- OS2 Boot Sector
* How It Works -- Partition Tables


OS2 BOOT SECTOR

Note:  I'll leave it to someone else to provide you with a
disassembly of an OS/2 HPFS boot sector, or a Linux boot sector,
or a WinNT boot sector, etc.

This article is a disassembly of a floppy or hard disk boot
sector for OS/2.  Apparently OS/2 uses the same boot sector for
both environments.  Basically a bootable FAT hard disk partition
looks like a big floppy during the early stages of the system's
boot processing.  This sector is at cylinder 0, head 0, sector 1
of a floppy or it is the first sector within a FAT hard disk
partition.  OS/2 floppy disk and hard disk boot sectors are
created by the OS/2 FORMAT program.

At the completion of your system's Power On Self Test (POST), INT
19 is called.  Usually INT 19 tries to read a boot sector from
the first floppy drive.  If a boot sector is found on the floppy
disk, the that boot sector is read into memory at location
0000:7C00 and INT 19 jumps to memory location 0000:7C00.
However, if no boot sector is found on the first floppy drive,
INT 19 tries to read the MBR from the first hard drive.  If an
MBR is found it is read into memory at location 0000:7c00 and INT
19 jumps to memory location 0000:7c00.  The small program in the
MBR will attempt to locate an active (bootable) partition in its
partition table.  If such a partition is found, the boot sector
of that partition is read into memory at location 0000:7C00 and
the MBR program jumps to memory location 0000:7C00.  Each
operating system has its own boot sector format.  The small
program in the boot sector must locate the first part of the
operating system's kernel loader program (or perhaps the kernel
itself or perhaps a "boot manager program") and read that into
memory.

INT 19 is also called when the CTRL-ALT-DEL keys are used.  On
most systems, CTRL-ALT-DEL causes an short version of the POST to
be executed before INT 19 is called.

=====

Where stuff is:

   The BIOS Parameter Block (BPB) starts at offset 0.
   The boot sector program starts at offset 46.
   The messages issued by this program start at offset 198.
   The OS/2 boot loader file name starts at offset 1d5.
   The boot sector signature is at offset 1fe.

Here is a summary of what this thing does:

 1) If booting from a hard disk partition, skip to step 6.
 2) Copy Diskette Parameter Table which is pointed to by INT 1E
    to the top of memory.
 3) Alter the copy of the Diskette Parameter Table.
 4) Alter INT 1E to point to altered Diskette Parameter Table at
    the top of memory.
 5) Do INT 13 AH=00, disk reset call so that the altered
    Diskette Parameter Table is used.
 6) Compute sector address of the root directory.
 7) Read the entire root directory into memory starting at
    location 1000:0000.
 8) Search the root directory entires for the file OS2BOOT.
 9) Read the OS2BOOT file into memory at 0800:0000.
10) Do a far return to enter the OS2BOOT program at 0800:0000.

NOTES:

   This program uses the CHS based INT 13H AH=02 to read the FAT
   root directory and to read the OS2BOOT file.  If the drive is
   >528MB, this CHS must be a translated CHS (or L-CHS, see my
   BIOS TYPES document).  Except for internal computations no
   addresses in LBA form are used, another reason why LBA doesn't
   solve the >528MB problem.

=====

Here is the entire sector in hex and ascii.

OFFSET 0 1 2 3  4 5 6 7  8 9 A B  C D E F  *0123456789ABCDEF*
000000 eb449049 424d2032 302e3000 02100100 *.D.IBM 20.0.....*
000010 02000200 00f8d800 3e000e00 3e000000 *........>...>...*
000020 06780d00 80002900 1c0c234e 4f204e41 *.x....)...#NO NA*
000030 4d452020 20204641 54202020 20200000 *ME    FAT     ..*
000040 00100000 0000fa33 db8ed3bc ff7bfbba *.......3.....{..*
000050 c0078eda 803e2400 00753d1e b840008e *.....>$..u=..@..*
000060 c026ff0e 1300cd12 c1e0068e c033ff33 *.&...........3.3*
000070 c08ed8c5 367800fc b90b00f3 a41fa118 *....6x..........*
000080 0026a204 001e33c0 8ed8a378 008c067a *.&....3....x...z*
000090 001f8a16 2400cd13 a0100098 f7261600 *....$........&..*
0000a0 03060e00 5091b820 00f72611 008b1e0b *....P.. ..&.....*
0000b0 0003c348 f7f35003 c1a33e00 b800108e *...H..P...>.....*
0000c0 c033ff59 890e4400 58a34200 33d2e873 *.3.Y..D.X.B.3..s*
0000d0 0033db8b 0e11008b fb51b90b 00bed501 *.3.......Q......*
0000e0 f3a65974 0583c320 e2ede335 268b471c *..Yt... ...5&.G.*
0000f0 268b571e f7360b00 fec08ac8 268b571a *&.W..6......&.W.*
000100 4a4aa00d 0032e4f7 e203063e 0083d200 *JJ...2.....>....*
000110 bb00088e c333ff06 57e82800 8d360b00 *.....3..W.(..6..*
000120 cbbe9801 eb03bead 01e80900 bec201e8 *................*
000130 0300fbeb feac0ac0 7409b40e bb0700cd *........t.......*
000140 10ebf2c3 50525103 061c0013 161e00f7 *....PRQ.........*
000150 361800fe c28ada33 d2f7361a 008afa8b *6......3..6.....*
000160 d0a11800 2ac34050 b402b106 d2e60af3 *....*.@P........*
000170 8bca86e9 8a162400 8af78bdf cd1372a6 *......$.......r.*
000180 5b598bc3 f7260b00 03f85a58 03c383d2 *[Y...&....ZX....*
000190 002acb7f afc31200 4f532f32 20212120 *.*......OS/2 !! *
0001a0 53595330 31343735 0d0a0012 004f532f *SYS01475.....OS/*
0001b0 32202121 20535953 30323032 350d0a00 *2 !! SYS02025...*
0001c0 12004f53 2f322021 21205359 53303230 *..OS/2 !! SYS020*
0001d0 32370d0a 004f5332 424f4f54 20202020 *27...OS2BOOT    *
0001e0 00000000 00000000 00000000 00000000 *................*
0001f0 00000000 00000000 00000000 000055aa *..............U.*

=====

The first 62 bytes of a boot sector are known as the BIOS
Parameter Block (BPB).  Here is the layout of the BPB fields
and the values they are assigned in this boot sector:

   db JMP instruction      at 7c00 size  2 = eb44
   db NOP instruction         7c02       1   90
   db OEMname                 7c03       8   'IBM 20.0'
   dw bytesPerSector          7c0b       2   0200
   db sectPerCluster          7c0d       1   01
   dw reservedSectors         7c0e       2   0001
   db numFAT                  7c10       1   02
   dw numRootDirEntries       7c11       2   0200
   dw numSectors              7c13       2   0000 (use numSectorsHuge)
   db mediaType               7c15       1   f8
   dw numFATsectors           7c16       2   00d8
   dw sectorsPerTrack         7c18       2   003e
   dw numHeads                7c1a       2   000e
   dd numHiddenSectors        7c1c       4   00000000
   dd numSectorsHuge          7c20       4   000d7806
   db driveNum                7c24       1   80
   db reserved                7c25       1   00
   db signature               7c26       1   29
   dd volumeID                7c27       4   001c0c23
   db volumeLabel             7c2b      11   'NO NAME   '
   db fileSysType             7c36       8   'FAT     '

=====

Here is the boot sector...

The first 3 bytes of the BPB are JMP and NOP instructions.

0000:7C00 EB44        JMP     START
0000:7C02 90          NOP

Here is the rest of the BPB.

0000:7C00 eb449049 424d2032 302e3000 02100100 *.D.IBM 20.0.....*
0000:7C10 02000200 00f8d800 3e000e00 3e000000 *........>...>...*
0000:7C20 06780d00 80002900 1c0c234e 4f204e41 *.x....)...#NO NA*
0000:7C30 4d452020 20204641 54202020 20200000 *ME    FAT     ..*

Additional data areas.

0000:7C30 ........ ........ ........ ....0000 *              ..*
0000:7C40 00100000 0000.... ........ ........ *......          *

   Note:

   0000:7c3e (DS:003e) = number of sectors in the FATs and root dir.
   0000:7c42 (DS:0042) = number of sectors in the FAT.
   0000:7c44 (DS:0044) = number of sectors in the root dir.

               START:                        START OF BOOT SECTOR PROGRAM

0000:7C46 FA          CLI                          interrupts off
0000:7C47 33DB        XOR     BX,BX                zero BX
0000:7C49 8ED3        MOV     SS,BX                SS now zero
0000:7C4B BCFF7B      MOV     SP,7BFF              SP now 7bff
0000:7C4E FB          STI                          interrupts on
0000:7C4F BAC007      MOV     DX,07C0              set DX to
0000:7C52 8EDA        MOV     DS,DX                   07c0

                              Are we booting from a floppy or a
                              hard disk partition?

0000:7C54 803E240000  CMP     BYTE PTR [0024],00   is driveNum in BPB 00?
0000:7C59 753D        JNZ     NOT_FLOPPY           jmp if not zero

                              We are booting from a floppy.  The
                              Diskette Parameter Table must be
                              copied and altered...

   Diskette Parameter Table is pointed to by INT 1E.  This
   program moves this table to high memory, alters the table, and
   changes INT 1E to point to the altered table.

   This table contains the following data:

   ????:0000 = Step rate and head unload time.
   ????:0001 = Head load time and DMA mode flag.
   ????:0002 = Delay for motor turn off.
   ????:0003 = Bytes per sector.
   ????:0004 = Sectors per track.
   ????:0005 = Intersector gap length.
   ????:0006 = Data length.
   ????:0007 = Intersector gap length during format.
   ????:0008 = Format byte value.
   ????:0009 = Head settling time.
   ????:000a = Delay until motor at normal speed.

                              Compute a valid high memory address.

0000:7C5B 1E          PUSH    DS                   save DS
0000:7C5C B84000      MOV     AX,0040              set ES
0000:7C5F 8EC0        MOV     ES,AX                   to 0040 (BIOS data area)
0000:7C61 26          ES:                          reduce system memory
0000:7C62 FF0E1300    DEC     WORD PTR [0013]         size by 1024
0000:7C66 CD12        INT     12                   get system memory size
0000:7C68 C1E06       SHL     AX,06                shift AX (mult by 64)
0000:7C6B 8EC0        MOV     ES,AX                move to ES
0000:7C6D 33FF        XOR     DI,DI                zero DI

                              Move the diskette param table to high memory.

0000:7C6F 33C0        XOR     AX,AX                zero AX
0000:7C71 8ED8        MOV     DS,AX                DS now zero
0000:7C73 C5367800    LDS     SI,[0078]            DS:SI = INT 1E vector
0000:7C77 FC          CLD                          clear direction
0000:7C78 B90B00      MOV     CX,000B              count is 11
0000:7C7B F3          REPZ                         copy diskette param table
0000:7C7C A4          MOVSB                           to top of memory

                              Alter the number of sectors per track
                              in the diskette param table in high memory.

0000:7C7D 1F          POP     DS                   restore DS
0000:7C7E A11800      MOV     AX,[0018]            get sectorsPerTrack from BPB
0000:7C81 26          ES:                          alter sectors per track
0000:7C82 A20400      MOV     [0004],AL               in diskette param table

                              Change INT 1E to point to altered diskette
                              param table and do a INT 13 disk reset call.

0000:7C85 1E          PUSH    DS                   save DS
0000:7C86 33C0        XOR     AX,AX                AX now zero
0000:7C88 8ED8        MOV     DS,AX                DS no zero
0000:7C8A A37800      MOV     [0078],AX            alter INT 1E vector
0000:7C8D 8C067A00    MOV     [007A],ES               to point to altered
                                                      diskette param table
0000:7C91 1F          POP     DS                   restore DS
0000:7C92 8A162400    MOV     DL,[0024]            driveNum from BPB
0000:7C96 CD13        INT     13                   diskette reset

         NOT_FLOPPY:

                              Compute the location and the size of
                              the root directory.  Read the entire
                              root directory into memory.

0000:7C98 A01000      MOV     AL,[0010]            get numFAT
0000:7C9B 98          CBW                          make into a word
0000:7C9C F7261600    MUL     WORD PTR [0016]      mult by numFatSectors
0000:7CA0 03060E00    ADD     AX,[000E]            add reservedSectors
0000:7CA4 50          PUSH    AX                   save
0000:7CA5 91          XCHG    CX,AX                move to CX
0000:7CA6 B82000      MOV     AX,0020              dir entry size
0000:7CA9 F7261100    MUL     WORD PTR [0011]      mult by numRootDirEntries
0000:7CAD 8B1E0B00    MOV     BX,[000B]            get bytesPerSector
0000:7CB1 03C3        ADD     AX,BX                add
0000:7CB3 48          DEC     AX                   subtract 1
0000:7CB4 F7F3        DIV     BX                   div by bytesPerSector
0000:7CB6 50          PUSH    AX                   save number of dir sectors
0000:7CB7 03C1        ADD     AX,CX                add number of fat sectors
0000:7CB9 A33E00      MOV     [003E],AX            save
0000:7CBC B80010      MOV     AX,1000              AX is now 1000
0000:7CBF 8EC0        MOV     ES,AX                ES is now 1000
0000:7CC1 33FF        XOR     DI,DI                DI is now zero
0000:7CC3 59          POP     CX                   get number dir sectors
0000:7CC4 890E4400    MOV     [0044],CX            save
0000:7CC8 58          POP     AX                   get number fat sectors
0000:7CC9 A34200      MOV     [0042],AX            save
0000:7CCC 33D2        XOR     DX,DX                DX now zero
0000:7CCE E87300      CALL    READ_SECTOR          read 1st sect of root dir
0000:7CD1 33DB        XOR     BX,BX                BX is now zero
0000:7CD3 8B0E1100    MOV     CX,[0011]            number of root dir entries

         DIR_SEARCH:                         SEARCH FOR OS2BOOT.

                              Search the root directory for the file
                              name OS2BOOT.

0000:7CD7 8BFB        MOV     DI,BX                DI is dir entry addr
0000:7CD9 51          PUSH    CX                   save CX
0000:7CDA B90B00      MOV     CX,000B              count is 11
0000:7CDD BED501      MOV     SI,01D5              addr of "OS2BOOT"
0000:7CE0 F3          REPZ                         is 1st dir entry
0000:7CE1 A6          CMPSB                           for "OS2BOOT"?
0000:7CE2 59          POP     CX                   restore CX
0000:7CE3 7405        JZ      FOUND_OS2BOOT        jmp if OS2BOOT
0000:7CE5 83C320      ADD     BX,+20               incr to next dir entry
0000:7CE8 E2ED        LOOP    DIR_SEARCH           try again

      FOUND_OS2BOOT:                         FOUND OS2BOOT.

                              OS2BOOT was found.  Get the starting
                              cluster number and convert to a sector
                              address.  Read OS2BOOT into memory and
                              finally do a far return to enter
                              the OS2BOOT program.

0000:7CEA E335        JCXZ    FAILED1              JMP if CX zero.
0000:7CEC 26          ES:                          get the szie of
0000:7CED 8B471C      MOV     AX,[BX+1C]              the OS2BOOT file
0000:7CF0 26          ES:                             from the OS2BOOT
0000:7CF1 8B571E      MOV     DX,[BX+1E]              directory entry
0000:7CF4 F7360B00    DIV     WORD PTR [000B]      div by bytesPerSect
0000:7CF8 FEC0        INC     AL                   add 1
0000:7CFA 8AC8        MOV     CL,AL                num sectors OS2BOOT
0000:7CFC 26          ES:                          get the starting
0000:7CFD 8B571A      MOV     DX,[BX+1A]              cluster number
0000:7D00 4A          DEC     DX                   subtract 1
0000:7D01 4A          DEC     DX                   subtract 1
0000:7D02 A00D00      MOV     AL,[000D]            sectorsPerCluster
0000:7D05 32E4        XOR     AH,AH                mutiply
0000:7D07 F7E2        MUL     DX                      to get LBA
0000:7D09 03063E00    ADD     AX,[003E]            add number of FAT sectors
0000:7D0D 83D200      ADC     DX,+00                  to LBA
0000:7D10 BB0008      MOV     BX,0800              set ES
0000:7D13 8EC3        MOV     ES,BX                   to 0800
0000:7D15 33FF        XOR     DI,DI                set ES:DI to entry point
0000:7D17 06          PUSH    ES                      address of
0000:7D18 57          PUSH    DI                         OS2BOOT
0000:7D19 E82800      CALL    READ_SECTOR          read OS2BOOT into memory
0000:7D1C 8D360B00    LEA     SI,[000B]            set DS:SI
0000:7D20 CB          RETF                         "far return" to OS2BOOT

            FAILED1:                            OS2BOOT WAS NOT FOUND.

0000:7D21 BE9801      MOV     SI,0198              "SYS01475" message
0000:7D24 EB03        JMP     FAILED3

            FAILED2:                            ERROR FROM INT 13.

0000:7D26 BEAD01      MOV     SI,01AD              "SYS02025" message

            FAILED3:                            OUTPUT ERROR MESSAGES.

0000:7D29 E80900      CALL    MSG_LOOP             display message
0000:7D2C BEC201      MOV     SI,01C2              "SYS02027" message
0000:7D2F E80300      CALL    MSG_LOOP             display message
0000:7D32 FB          STI                          interrupts on

               HANG:                            HANG THE SYSTEM!

0000:7D33 EBFE        JMP     HANG                 sit and stay!

           MSG_LOOP:                         DISPLAY AN ERROR MESSAGE.

                              Routine to display the message
                              text pointed to by SI.

0000:7D35 AC          LODSB                        get next char of message
0000:7D36 0AC0        OR      AL,AL                end of message?
0000:7D38 7409        JZ      RETURN               jmp if yes
0000:7D3A B40E        MOV     AH,0E                write 1 char
0000:7D3C BB0700      MOV     BX,0007              video attributes
0000:7D3F CD10        INT     10                   INT 10 to write 1 char
0000:7D41 EBF2        JMP     MSG_LOOP             do again

             RETURN:

0000:7D43 C3          RET                          return

       READ_SECTOR:                          ROUTINE TO READ SECTORS.

                              Read sectors into memory.  Read multiple
                              sectors but don't read across a track
                              boundary.

                              The caller supplies the following:
                                 DX:AX = sector address to read (as LBA)
                                    CX = number of sectors to read
                                 ES:DI = memory address to read into

0000:7D44 50          PUSH    AX                   save lower part of LBA
0000:7D45 52          PUSH    DX                   save upper part of LBA
0000:7D46 51          PUSH    CX                   save number of sect to read
0000:7D47 03061C00    ADD     AX,[001C]            add numHiddenSectors
0000:7D4B 13161E00    ADC     DX,[001E]               to LBA
0000:7D4F F7361800    DIV     WORD PTR [0018]      div by sectorsPerTrack
0000:7D53 FEC2        INC     DL                   add 1 to sector number
0000:7D55 8ADA        MOV     BL,DL                save sector number
0000:7D57 33D2        XOR     DX,DX                zero upper part of LBA
0000:7D59 F7361A00    DIV     WORD PTR [001A]      div by numHeads
0000:7D5D 8AFA        MOV     BH,DL                save head number
0000:7D5F 8BD0        MOV     DX,AX                save cylinder number
0000:7D61 A11800      MOV     AX,[0018]            sectorsPerTrack
0000:7D64 2AC3        SUB     AL,BL                sub sector number
0000:7D66 40          INC     AX                   add 1
0000:7D67 50          PUSH    AX                   save number of sector to read
0000:7D68 B402        MOV     AH,02                INT 13 read sectors
0000:7D6A B106        MOV     CL,06                shift count
0000:7D6C D2E6        SHL     DH,CL                shift high cyl left
0000:7D6E 0AF3        OR      DH,BL                or in sector number
0000:7D70 8BCA        MOV     CX,DX                move cyl/sect to CX
0000:7D72 86E9        XCHG    CH,CL                swap cyl/sect
0000:7D74 8A162400    MOV     DL,[0024]            driveNum
0000:7D78 8AF7        MOV     DH,BH                head number
0000:7D7A 8BDF        MOV     BX,DI                memory addr to read into
0000:7D7C CD13        INT     13                   INT 13 read sectors call
0000:7D7E 72A6        JB      FAILED2              jmp if any error
0000:7D80 5B          POP     BX                   get number of sectors read
0000:7D81 59          POP     CX                   restore CX
0000:7D82 8BC3        MOV     AX,BX                number of sector to AX
0000:7D84 F7260B00    MUL     WORD PTR [000B]      multiply by sector size
0000:7D88 03F8        ADD     DI,AX                add to memory address
0000:7D8A 5A          POP     DX                   restore upper part of LBA
0000:7D8B 58          POP     AX                   restore lower part of LBA
0000:7D8C 03C3        ADD     AX,BX                add number of sector just
0000:7D8E 83D200      ADC     DX,+00                  read to LBA
0000:7D91 2ACB        SUB     CL,BL                decr requested num of sect
0000:7D93 7FAF        JG      READ_SECTOR          jmp if not zero
0000:7D95 C3          RET                          return

Data not used.

0000:7D90 ........ ....1200 ........ ........ *      ..        *

Messages here.

0000:7D90 ........ ........ 4f532f32 20212120 *        OS/2 !! *
0000:7Da0 53595330 31343735 0d0a0012 004f532f *SYS01475.....OS/*
0000:7Db0 32202121 20535953 30323032 350d0a00 *2 !! SYS02025...*
0000:7Dc0 12004f53 2f322021 21205359 53303230 *..OS/2 !! SYS020*
0000:7Dd0 32370d0a 00...... ........ ........ *27...           *

OS/2 loader file name.

0000:7Dd0 ........ ..4f5332 424f4f54 20202020 *     OS2BOOT    *

Data not used.

0000:7De0 00000000 00000000 00000000 00000000 *................*
0000:7Df0 00000000 00000000 00000000 0000.... *..............  *

The last two bytes contain a 55AAH signature.

0000:7Df0 ........ ........ ........ ....55aa *              U.*

/end/
-- 
\\===============\\=======================\\
 \\  Hale Landis  \\      303-548-0567     \\
 // Niwot, CO USA // landis@sugs.tware.com //
//===============//=======================//