]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/cpu/tssring.asm
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / cpu / tssring.asm
1 ; tssring.asm
2 ;
3 ; Test program: 80386 task switching (Task State Segments) to jump privilege levels
4 ; (C) 2010-2012 Jonathan Campbell.
5 ; Hackipedia DOS library.
6 ;
7 ; This code is licensed under the LGPL.
8 ; <insert LGPL legal text here>
9 ;
10 ; proot of concept:
11 ; switching the CPU into 386 16-bit protected mode (and back)
12 ; while playing with the Task State Segment mechanism to
13 ; demonstrate hopping between "ring 0" and "ring 3".
14 ; note that the 286 TSS is very similar (with different values
15 ; for the TYPE fields in the GDT, and different register layout)
16 ; so I will not (yet) bother making a 286 version.
17 bits 16                 ; 16-bit real mode
18 org 0x100               ; MS-DOS .COM style
19
20 ; assume ES == DS and SS == DS and DS == CS
21
22 ; SELECTORS
23 NULL_SEL        equ             0
24 CODE_SEL        equ             8
25 DATA_SEL        equ             16
26 VIDEO_SEL       equ             24
27 CODE32_SEL      equ             32
28 DATA32_SEL      equ             40
29 TSS_SEL         equ             48
30 TSS_2_SEL       equ             56
31 TSS_3_SEL       equ             64
32 LDT_SEL         equ             72
33 CODE_SEL3       equ             80
34 DATA_SEL3       equ             88
35 VIDEO_SEL3      equ             96
36 MAX_SEL         equ             104
37
38 ; ===== ENTRY POINT
39                 call            cpu_is_386
40                 je              is_386
41                 mov             dx,str_cpu_not_386
42                 jmp             exit2dos_with_message
43 is_386:
44
45 ; ===== CHECK FOR VIRTUAL 8086 MODE
46                 smsw            ax                      ; 386 or higher: If we're in real mode
47                 test            al,1                    ; and bit 0 is already set, we're in virtual 8086
48                 jz              is_realmode             ; and our switch to prot mode will cause problems.
49                 mov             dx,str_cpu_v86_mode
50                 jmp             exit2dos_with_message
51 is_realmode:
52
53 ; ===== WE NEED TO PATCH SOME OF OUR OWN CODE
54                 mov             ax,cs
55                 mov             word [real_entry_patch+3],ax    ; overwrite segment field of JMP SEG:OFF
56
57 ; ===== BUILD THE GLOBAL DESCRIPTOR TABLE AND GDTR REGISTER
58                 mov             ax,cs
59                 mov             bx,ax
60                 shr             bx,12
61                 shl             ax,4                    ; BX:AX = 32-bit physical addr of our segment
62                 mov             word [MY_PHYS_BASE],ax
63                 mov             word [MY_PHYS_BASE+2],bx
64
65                 add             ax,GDT
66                 adc             bx,0                    ; BX:AX += offset of GDT
67
68                 mov             word [GDTR],MAX_SEL - 1
69                 mov             word [GDTR+2],ax
70                 mov             word [GDTR+4],bx        ; GDTR: limit MAX_SEL-1 base=physical mem addr of GDT
71
72                 mov             ax,word [MY_PHYS_BASE]
73                 mov             bx,word [MY_PHYS_BASE+2]
74                 add             ax,IDT
75                 adc             bx,0
76
77                 mov             word [IDTR],2047
78                 mov             word [IDTR+2],ax
79                 mov             word [IDTR+4],bx
80
81                 cld
82
83 ;     zero IDT
84                 mov             di,IDT
85                 mov             cx,1023
86                 xor             ax,ax
87                 rep             stosw
88
89                 mov             di,GDT
90 ;     NULL selector (NULL_SEL)
91                 xor             ax,ax
92                 stosw
93                 stosw
94                 stosw
95                 stosw
96 ;     Code selector (CODE_SEL)
97                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
98                 stosw                                   ; LIMIT
99                 mov             ax,[MY_PHYS_BASE]
100                 stosw                                   ; BASE[15:0]
101                 mov             al,[MY_PHYS_BASE+2]
102                 mov             ah,0x9A
103                 stosw                                   ; BASE[23:16] access byte=executable readable
104                 mov             al,0x0F
105                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
106                 stosw
107 ;     Data selector (DATA_SEL)
108                 xor             ax,ax
109                 dec             ax                      ; 0xFFFF
110                 stosw                                   ; LIMIT
111                 mov             ax,[MY_PHYS_BASE]
112                 stosw                                   ; BASE[15:0]
113                 mov             al,[MY_PHYS_BASE+2]
114                 mov             ah,0x92
115                 stosw                                   ; BASE[23:16] access byte=data writeable
116                 mov             al,0x0F
117                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
118                 stosw
119 ;     Data selector (VIDEO_SEL)
120                 xor             ax,ax
121                 dec             ax                      ; 0xFFFF
122                 stosw                                   ; LIMIT
123                 mov             ax,0x8000
124                 stosw                                   ; BASE[15:0]
125                 mov             al,0x0B                 ; BASE=0xB8000
126                 mov             ah,0x92
127                 stosw                                   ; BASE[23:16] access byte=data writeable
128                 mov             al,0x0F
129                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
130                 stosw
131 ;     Code selector (32-bit) (CODE32_SEL)
132                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
133                 stosw                                   ; LIMIT
134                 mov             ax,[MY_PHYS_BASE]
135                 stosw                                   ; BASE[15:0]
136                 mov             al,[MY_PHYS_BASE+2]
137                 mov             ah,0x9E
138                 stosw                                   ; BASE[23:16] access byte=executable readable conforming
139                 mov             al,0xCF
140                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
141                 stosw
142 ;     Data selector (32-bit) (DATA32_SEL)
143                 xor             ax,ax
144                 dec             ax                      ; 0xFFFF
145                 stosw                                   ; LIMIT
146                 mov             ax,[MY_PHYS_BASE]
147                 stosw                                   ; BASE[15:0]
148                 mov             al,[MY_PHYS_BASE+2]
149                 mov             ah,0x92
150                 stosw                                   ; BASE[23:16] access byte=data writeable
151                 mov             al,0xCF
152                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
153                 stosw
154 ;     TSS selector (32-bit) (TSS_SEL)
155                 mov             ax,TSS_AREA_SIZE - 1
156                 stosw                                   ; LIMIT
157                 mov             ax,[MY_PHYS_BASE]
158                 mov             bx,[MY_PHYS_BASE+2]
159                 add             ax,TSS_AREA
160                 adc             bx,0
161                 stosw                                   ; BASE[15:0]
162                 mov             al,bl
163                 mov             ah,0x89                 ; present, non-segment, type=9 (TSS busy)
164                 stosw                                   ; BASE[23:16] access byte=data writeable
165                 mov             al,0x00
166                 mov             ah,bh                   ; LIMIT[19:16] flags=granular BASE[31:24]
167                 stosw
168 ;     TSS selector (32-bit) (TSS_2_SEL)
169                 mov             ax,TSS_AREA_2_SIZE - 1
170                 stosw                                   ; LIMIT
171                 mov             ax,[MY_PHYS_BASE]
172                 mov             bx,[MY_PHYS_BASE+2]
173                 add             ax,TSS_AREA_2
174                 adc             bx,0
175                 stosw                                   ; BASE[15:0]
176                 mov             al,bl
177                 mov             ah,0x89                 ; present, non-segment, type=9 (TSS non busy)
178                 stosw                                   ; BASE[23:16] access byte=data writeable
179                 mov             al,0x00
180                 mov             ah,bh                   ; LIMIT[19:16] flags=granular BASE[31:24]
181                 stosw
182 ;     TSS selector (32-bit) (TSS_3_SEL)
183                 mov             ax,TSS_AREA_3_SIZE - 1
184                 stosw                                   ; LIMIT
185                 mov             ax,[MY_PHYS_BASE]
186                 mov             bx,[MY_PHYS_BASE+2]
187                 add             ax,TSS_AREA_3
188                 adc             bx,0
189                 stosw                                   ; BASE[15:0]
190                 mov             al,bl
191                 mov             ah,0x89                 ; present, non-segment, type=9 (TSS non busy)
192                 stosw                                   ; BASE[23:16] access byte=data writeable
193                 mov             al,0x00
194                 mov             ah,bh                   ; LIMIT[19:16] flags=granular BASE[31:24]
195                 stosw
196 ;     LDT selector (32-bit) (LDT_SEL)
197                 mov             ax,LDT_AREA_SIZE - 1
198                 stosw                                   ; LIMIT
199                 mov             ax,[MY_PHYS_BASE]
200                 mov             bx,[MY_PHYS_BASE+2]
201                 add             ax,LDT_AREA
202                 adc             bx,0
203                 stosw                                   ; BASE[15:0]
204                 mov             al,bl
205                 mov             ah,0x82                 ; present, non-segment, type=2 (LDT)
206                 stosw                                   ; BASE[23:16] access byte=data writeable
207                 mov             al,0x00
208                 mov             ah,bh                   ; LIMIT[19:16] flags=granular BASE[31:24]
209                 stosw
210 ;     Code selector (CODE_SEL3)
211                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
212                 stosw                                   ; LIMIT
213                 mov             ax,[MY_PHYS_BASE]
214                 stosw                                   ; BASE[15:0]
215                 mov             al,[MY_PHYS_BASE+2]
216                 mov             ah,0xFA
217                 stosw                                   ; BASE[23:16] access byte=executable readable DPL=3
218                 mov             al,0x0F
219                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
220                 stosw
221 ;     Data selector (DATA_SEL3)
222                 xor             ax,ax
223                 dec             ax                      ; 0xFFFF
224                 stosw                                   ; LIMIT
225                 mov             ax,[MY_PHYS_BASE]
226                 stosw                                   ; BASE[15:0]
227                 mov             al,[MY_PHYS_BASE+2]
228                 mov             ah,0xF2
229                 stosw                                   ; BASE[23:16] access byte=data writeable DPL=3
230                 mov             al,0x0F
231                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
232                 stosw
233 ;     Data selector (VIDEO_SEL3)
234                 xor             ax,ax
235                 dec             ax                      ; 0xFFFF
236                 stosw                                   ; LIMIT
237                 mov             ax,0x8000
238                 stosw                                   ; BASE[15:0]
239                 mov             al,0x0B                 ; BASE=0xB8000
240                 mov             ah,0xF2
241                 stosw                                   ; BASE[23:16] access byte=data writeable
242                 mov             al,0x0F
243                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
244                 stosw
245
246 ; load CPU registers
247                 cli                                     ; disable interrupts
248                 lgdt            [GDTR]                  ; load into processor GDTR
249                 lidt            [IDTR]
250
251 ; switch into protected mode
252                 mov             eax,0x00000001
253                 mov             cr0,eax
254                 jmp             CODE_SEL:prot_entry
255 prot_entry:     mov             ax,DATA_SEL             ; now reload the segment registers
256                 mov             ds,ax
257                 mov             es,ax
258                 mov             fs,ax
259                 mov             gs,ax
260                 mov             ss,ax
261                 mov             sp,0xFFF0
262                 
263 ; load LDT
264                 mov             ax,LDT_SEL
265                 lldt            ax
266                 
267 ; zero the first TSS
268                 cld
269                 mov             edi,TSS_AREA
270                 mov             ecx,TSS_AREA_SIZE / 4
271                 xor             eax,eax
272                 rep             stosd
273
274 ; zero the second TSS
275                 cld
276                 mov             edi,TSS_AREA_2
277                 mov             ecx,TSS_AREA_2_SIZE / 4
278                 xor             eax,eax
279                 rep             stosd
280
281 ; set up the task register. for now, leave it at the first one.
282                 mov             ax,TSS_SEL
283                 ltr             ax
284
285 ; prepare the second one
286                 cld
287                 xor             eax,eax         ; prepare EAX=0
288                 mov             ebx,eax
289                 mov             ecx,0x12345678  ; check value
290                 mov             edi,TSS_AREA_2
291                 stosd                           ; TSS+0x00 = back link
292                 mov             ax,0xF000
293                 stosd                           ; TSS+0x04 = ESP0
294                 mov             ax,DATA_SEL
295                 stosd                           ; TSS+0x08 = SS0
296                 mov             ax,0xF000
297                 stosd                           ; TSS+0x0C = ESP1
298                 mov             ax,DATA_SEL
299                 stosd                           ; TSS+0x10 = SS1
300                 mov             ax,0xF000
301                 stosd                           ; TSS+0x14 = ESP2
302                 mov             ax,DATA_SEL
303                 stosd                           ; TSS+0x18 = SS2
304                 xor             ax,ax
305                 stosd                           ; TSS+0x1C = CR3
306                 mov             eax,tss_jump_1
307                 stosd                           ; TSS+0x20 = EIP
308                 pushfd
309                 pop             eax
310                 stosd                           ; TSS+0x24 = EFLAGS
311                 xor             eax,eax
312                 stosd                           ; TSS+0x28 = EAX
313                 stosd                           ; TSS+0x2C = ECX
314                 stosd                           ; TSS+0x30 = EDX
315                 stosd                           ; TSS+0x34 = EBX
316                 mov             ax,0xF000
317                 stosd                           ; TSS+0x38 = ESP
318                 xor             ax,ax
319                 stosd                           ; TSS+0x3C = EBP
320                 stosd                           ; TSS+0x40 = ESI
321                 stosd                           ; TSS+0x44 = EDI
322                 mov             ax,DATA_SEL
323                 stosd                           ; TSS+0x48 = ES
324                 mov             ax,CODE_SEL
325                 stosd                           ; TSS+0x4C = CS
326                 mov             ax,DATA_SEL
327                 stosd                           ; TSS+0x50 = SS
328                 stosd                           ; TSS+0x54 = DS
329                 stosd                           ; TSS+0x58 = FS
330                 stosd                           ; TSS+0x5C = GS
331                 mov             ax,LDT_SEL
332                 stosd                           ; TSS+0x60 = LDT selector (meh, I don't use it anyway)
333                 xor             ax,ax
334                 stosd                           ; TSS+0x64 = I/O map base=0, T=0
335
336 ; now, SWITCH!
337                 jmp             TSS_2_SEL:0
338
339 ; TSS switch should end up HERE.
340 ; Task register now points to TSS_2_SEL as active task.
341 ; TEST: If the CPU truly loaded state from TSS_2_SEL, all general regs should be zero
342 tss_jump_1:     or              eax,ebx
343                 or              eax,ecx
344                 or              eax,edx
345                 or              eax,esi
346                 or              eax,edi
347                 jz              tss_jump_1_zero
348                 
349                 mov             ax,VIDEO_SEL
350                 mov             es,ax
351                 mov             word [es:0],0x4E30      ; '0'
352                 mov             al,3
353                 out             61h,al                  ; turn on bell
354                 hlt
355 tss_jump_1_zero:
356 ; TEST: All segment registers except CS should be DATA_SEL
357                 mov             ax,ds
358                 sub             ax,DATA_SEL
359                 
360                 mov             bx,es
361                 sub             bx,DATA_SEL
362                 or              ax,bx
363                 
364                 mov             bx,fs
365                 sub             bx,DATA_SEL
366                 or              ax,bx
367                 
368                 mov             bx,gs
369                 sub             bx,DATA_SEL
370                 or              ax,bx
371                 
372                 mov             bx,ss
373                 sub             bx,DATA_SEL
374                 or              ax,bx
375                 
376                 jz              tss_jump_1_sreg_ok
377                 
378                 mov             ax,VIDEO_SEL
379                 mov             es,ax
380                 mov             word [es:0],0x4E31      ; '1'
381                 mov             al,3
382                 out             61h,al                  ; turn on bell
383                 hlt
384 tss_jump_1_sreg_ok:
385
386 ; if the CPU truly saved state into TSS_SEL, the memory location
387 ; corresponding to ECX should be 0x12345678 (because we loaded ECX
388 ; with that value prior to switching state, remember?)
389                 cmp             dword [TSS_AREA+0x2C],0x12345678
390                 jz              tss_jump_1_ecx_ok
391                 
392                 mov             ax,VIDEO_SEL
393                 mov             es,ax
394                 mov             word [es:0],0x4E32      ; '2'
395                 mov             al,3
396                 out             61h,al                  ; turn on bell
397                 hlt
398 tss_jump_1_ecx_ok:
399
400 ; draw directly onto VGA alphanumeric RAM at 0xB8000
401                 cld
402                 push            es
403                 mov             ax,VIDEO_SEL
404                 mov             es,ax
405                 mov             si,vdraw_msg
406                 xor             di,di
407 vdraw1:         lodsb                                   ; AL = DS:SI++
408                 or              al,al
409                 jz              vdraw1e
410                 mov             ah,0x1E
411                 stosw                                   ; ES:DI = AX, DI += 2
412                 jmp             vdraw1
413 vdraw1e:        pop             es
414
415 ; now, jump into 32-bit protected mode
416                 jmp             CODE32_SEL:prot32_entry
417 bits 32
418 prot32_entry:   mov             ax,DATA32_SEL
419                 mov             ds,ax
420                 mov             es,ax
421                 mov             fs,ax
422                 mov             gs,ax
423                 mov             ss,ax
424                 mov             esp,0xFFF0
425
426 ; draw directly onto VGA alphanumeric RAM at 0xB8000
427                 cld
428                 mov             esi,vdraw32_msg
429                 mov             edi,0xB8000+(80*2)
430                 sub             edi,[MY_PHYS_BASE]
431 vdraw321:       lodsb                                   ; AL = DS:SI++
432                 or              al,al
433                 jz              vdraw321e
434                 mov             ah,0x1E
435                 stosw                                   ; ES:DI = AX, DI += 2
436                 jmp             vdraw321
437 vdraw321e:
438
439 ; jump 32-bit to 16-bit
440                 jmp             CODE_SEL:prot32_to_prot
441 bits 16
442 prot32_to_prot: mov             ax,DATA_SEL
443                 mov             ds,ax
444                 mov             es,ax
445                 mov             fs,ax
446                 mov             gs,ax
447                 mov             ss,ax
448
449 ; prepare the third one---ring 3
450                 cld
451                 xor             eax,eax         ; prepare EAX=0
452                 mov             ebx,eax
453                 mov             ecx,0x12345678  ; check value
454                 mov             edi,TSS_AREA_3
455                 stosd                           ; TSS+0x00 = back link
456                 mov             ax,0xF000
457                 stosd                           ; TSS+0x04 = ESP0
458                 mov             ax,DATA_SEL
459                 stosd                           ; TSS+0x08 = SS0
460                 mov             ax,0xF000
461                 stosd                           ; TSS+0x0C = ESP1
462                 mov             ax,DATA_SEL
463                 stosd                           ; TSS+0x10 = SS1
464                 mov             ax,0xF000
465                 stosd                           ; TSS+0x14 = ESP2
466                 mov             ax,DATA_SEL
467                 stosd                           ; TSS+0x18 = SS2
468                 xor             ax,ax
469                 stosd                           ; TSS+0x1C = CR3
470                 mov             eax,tss_jump_3
471                 stosd                           ; TSS+0x20 = EIP
472                 pushfd
473                 pop             eax
474                 stosd                           ; TSS+0x24 = EFLAGS
475                 xor             eax,eax
476                 stosd                           ; TSS+0x28 = EAX
477                 stosd                           ; TSS+0x2C = ECX
478                 stosd                           ; TSS+0x30 = EDX
479                 stosd                           ; TSS+0x34 = EBX
480                 mov             ax,0xF000
481                 stosd                           ; TSS+0x38 = ESP
482                 xor             ax,ax
483                 stosd                           ; TSS+0x3C = EBP
484                 stosd                           ; TSS+0x40 = ESI
485                 stosd                           ; TSS+0x44 = EDI
486                 mov             ax,DATA_SEL3 | 3
487                 stosd                           ; TSS+0x48 = ES
488                 mov             ax,CODE_SEL3 | 3
489                 stosd                           ; TSS+0x4C = CS
490                 mov             ax,DATA_SEL3 | 3
491                 stosd                           ; TSS+0x50 = SS
492                 stosd                           ; TSS+0x54 = DS
493                 stosd                           ; TSS+0x58 = FS
494                 stosd                           ; TSS+0x5C = GS
495                 mov             ax,LDT_SEL
496                 stosd                           ; TSS+0x60 = LDT selector (meh, I don't use it anyway)
497                 xor             ax,ax
498                 stosd                           ; TSS+0x64 = I/O map base=0, T=0
499 ; Call the TSS, so that we can IRET to return to RING 0
500                 call            TSS_3_SEL:0
501                 jmp             skip_tss_3
502
503 ; now we are 16-bit RING 3
504 tss_jump_3:     mov             ax,VIDEO_SEL3   ; PROVE IT
505                 mov             es,ax           ; BY WRITING TO SCREEN
506                 mov             si,vdraw3_msg
507                 mov             di,80*4
508                 cld
509 vdraw3:         lodsb                                   ; AL = DS:SI++
510                 or              al,al
511                 jz              vdraw3e
512                 mov             ah,0x1E
513                 stosw                                   ; ES:DI = AX, DI += 2
514                 jmp             vdraw3
515 vdraw3e:
516                 
517 ; return from ring 3
518                 iret
519
520 ; TSS RING-3 test COMPLETE
521 skip_tss_3:
522
523 ; active task is TSS_2_SEL. Prove we can switch tasks again by modifying
524 ; EIP in TSS_SEL, then switching tasks.
525                 mov             dword [TSS_AREA+0x20],tss_jump_2
526                 jmp             TSS_SEL:0
527 tss_jump_2:
528
529 ; having switched back to TSS_SEL, the value we left in ECX should still
530 ; be there.
531                 cmp             ecx,0x12345678
532                 jz              tss_jump_2_ecx_ok
533                 
534                 mov             ax,VIDEO_SEL
535                 mov             es,ax
536                 mov             word [es:0],0x4E33      ; '3'
537                 mov             al,3
538                 out             61h,al                  ; turn on bell
539                 hlt
540 tss_jump_2_ecx_ok:
541
542 ; switch back to real mode.
543 ; unlike the 286, switching back means clearing bit 0 of CR0
544                 xor             eax,eax                 ; clear bit 0
545                 mov             cr0,eax
546
547 real_entry_patch:jmp            0x0000:real_entry       ; the segment field is patched by code above
548 real_entry:     mov             ax,cs
549                 mov             ds,ax
550                 mov             es,ax
551                 mov             fs,ax
552                 mov             ss,ax
553                 mov             sp,0xFFF0
554
555 ; ===== REBUILD GDTR FOR PROPER REAL MODE OPERATION
556                 mov             word [GDTR],0xFFFF
557                 mov             word [GDTR+2],0
558                 mov             word [GDTR+4],0         ; GDTR: limit 0xFFFF base 0x00000000
559                 lgdt            [GDTR]                  ; load into processor GDTR
560
561                 mov             word [IDTR],0xFFFF
562                 mov             word [IDTR+2],0
563                 mov             word [IDTR+4],0         ; IDTR: limit 0xFFFF base 0x00000000
564                 lidt            [IDTR]
565
566 ; ====== PROVE WE MADE IT TO REAL MODE
567                 mov             si,vdraw2_msg
568                 mov             ax,0xB800
569                 mov             es,ax
570                 mov             di,80*6
571 vdraw2:         lodsb                                   ; AL = DS:SI++
572                 or              al,al
573                 jz              vdraw2e
574                 mov             ah,0x1E
575                 stosw                                   ; ES:DI = AX, DI += 2
576                 jmp             vdraw2
577 vdraw2e:        mov             ax,cs
578                 mov             es,ax
579
580                 sti
581
582 ; ===== DONE
583                 jmp             exit2dos
584
585 ; ===== EXIT TO DOS WITH ERROR MESSAGE DS:DX
586 exit2dos_with_message:
587                 mov             ah,9
588                 int             21h
589 ; ===== EXIT TO DOS
590 exit2dos:       mov             ax,4C00h
591                 int             21h
592
593 ; 8086 test: EFLAGS will always have bits 12-15 set
594 cpu_is_386:     pushf
595                 pop             ax
596                 and             ax,0x0FFF
597                 push            ax
598                 popf
599                 pushf
600                 pop             ax
601                 and             ax,0xF000
602                 cmp             ax,0xF000
603                 jz              cpu_is_386_not
604 ; 286 test: EFLAGS will always have bits 12-15 clear
605                 or              ax,0xF000
606                 push            ax
607                 popf
608                 pushf
609                 pop             ax
610                 and             ax,0xF000
611                 jz              cpu_is_386_not
612 ; it's a 386
613                 xor             ax,ax                   ; ZF=1
614                 ret
615 cpu_is_386_not: mov             ax,1
616                 or              ax,ax                   ; ZF=0
617                 ret
618
619 ; strings
620 str_cpu_not_386: db             "386 or higher required$"
621 str_cpu_v86_mode: db            "Virtual 8086 mode detected$"
622 vdraw2_msg:     db              "This message was drawn on screen back from real mode!",0
623 vdraw3_msg:     db              "This message was drawn on screen from 386 16-bit protected mode, ring 3!",0
624 vdraw_msg:      db              "This message was drawn on screen from 386 16-bit protected mode!",0
625 vdraw32_msg:    db              "This message was drawn on screen from 386 32-bit protected mode!",0
626
627 ; THESE VARIABLES DO NOT EXIST IN THE ACTUAL .COM FILE.
628 ; They exist in the yet-uninitialized area of RAM just beyond the
629 ; end of the loaded COM file image.
630                 align           8
631 RALLOC:         db              0xAA
632 GDTR            equ             RALLOC+0
633 IDTR            equ             GDTR+8
634 MY_PHYS_BASE    equ             IDTR+8
635 GDT             equ             MY_PHYS_BASE+8
636 IDT             equ             GDT+MAX_SEL
637   IDT_SIZE      equ             2048
638 TSS_AREA        equ             IDT+IDT_SIZE
639   TSS_AREA_SIZE equ             2048
640 TSS_AREA_2      equ             TSS_AREA+TSS_AREA_SIZE
641   TSS_AREA_2_SIZE equ           2048
642 TSS_AREA_3      equ             TSS_AREA_2+TSS_AREA_2_SIZE
643   TSS_AREA_3_SIZE equ           2048
644 LDT_AREA        equ             TSS_AREA_3+TSS_AREA_3_SIZE
645   LDT_AREA_SIZE equ             64
646