home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / PMTUT.ZIP / PMTUT002.ZIP / PMTUT.ASM < prev    next >
Encoding:
Assembly Source File  |  1995-09-13  |  10.4 KB  |  343 lines

  1. ;****************************************************************************
  2. ;* this file contains a simple PM switcher to illustrate the examples shown *
  3. ;* in PMTUT.TXT. Please do not spread this file without PMTUT.TXT!          *
  4. ;* USE AT YOUR OWN RISK!                            *
  5. ;*                                        *
  6. ;* Written by Till Gerken                            *
  7. ;*    (Internet: tig@ngo.ol.ni.schule.de)                    *
  8. ;****************************************************************************
  9.  
  10. ideal
  11. P386
  12.  
  13. ;----------------------------------------------------------------------------
  14.  
  15. STACK16_SIZE    =    100h        ; stack size for Real Mode
  16. STACK32_SIZE    =    100h        ; stack size for Protected Mode
  17.  
  18. struc segment_descriptor
  19.   seg_length0_15    dw    ?    ; low word of the segment length
  20.   base_addr0_15        dw    ?    ; low word of base address
  21.   base_addr16_23    db    ?    ; low byte of high word of base addr.
  22.   flags            db    ?    ; segment type and misc. flags
  23.   access        db    ?    ; highest nibble of segment length
  24.                       ; and access flags
  25.   base_addr24_31    db    ?    ; highest byte of base address
  26. ends segment_descriptor
  27.  
  28. struc interrupt_descriptor
  29.   offset0_15        dw    ?    ; low word of handler offset
  30.   selector0_15        dw    ?    ; segment selector
  31.   zero_byte        db    0    ; unused in this descriptor format
  32.   flags            db    ?    ; flag-byte
  33.   offset16_31        dw    ?    ; high-word of handler offset
  34. ends interrupt_descriptor
  35.  
  36. ;****************************************************************************
  37.  
  38. segment code16 para public use16    ; this segment contains all 16-bit
  39. assume cs:code16, ds:code16        ; code and data stuff
  40.  
  41. ;----------------------------------------------------------------------------
  42.  
  43. stack16        db    STACK16_SIZE dup (?)    ; 16-bit Real Mode stack
  44. label    stack16_end    word
  45.  
  46. idt_real    dw    3ffh,0,0        ; Real Mode IDT
  47.  
  48. ;----------------------------------------------------------------------------
  49. ; quick and dirty exit
  50. ; In:    DS:DX - pointer to '$' terminated exit message
  51. ; Out:    Difficult to say, the function never returns :)
  52.  
  53. proc    err16exit
  54.     mov    ah,9            ; select DOS' print string function
  55.     int    21h            ; print the msg
  56.     mov    ax,4cffh        ; exit with exit-code 0ffh
  57.     int    21h            ; good bye...
  58. endp    err16exit
  59.  
  60. ;----------------------------------------------------------------------------
  61. ; checks if the processor is at least a 80386
  62.  
  63. no386e        db    'Sorry, at least a 80386 is needed!',13,10,'$'
  64.  
  65. proc    check_processor
  66.     pushf            ; save flags for later
  67.     xor    ah,ah        ; clear high byte
  68.     push    ax        ; push AX on the stack
  69.     popf            ; pop this value into the flag register
  70.     pushf            ; push flags on the stack
  71.     pop    ax        ; ...and get flags into AX
  72.     and    ah,0f0h        ; try to set the high nibble
  73.     cmp    ah,0f0h        ; on a 80386, the high nibble can never be 0f0h
  74.     je    no386
  75.     mov    ah,70h        ; now try to set NT and IOPL
  76.     push    ax
  77.     popf
  78.     pushf
  79.     pop    ax
  80.     and    ah,70h        ; if they couldn't be modified, no 386 is installed
  81.     jz    no386
  82.     popf            ; restore flags
  83.     ret            ; and return
  84. no386:
  85.     mov    dx,offset no386e; if there is no 386, exit with error msg
  86.     jmp    err16exit
  87. endp    check_processor
  88.  
  89. ;----------------------------------------------------------------------------
  90. ; checks if we are running in Real Mode
  91.  
  92. nrme        db    'You are currently running in V86 mode!',13,10,'$'
  93.  
  94. proc    check_mode
  95.     mov    eax,cr0        ; get CR0 into EAX
  96.     and    al,1        ; check if PM bit is set
  97.     jnz    not_real_mode    ; it is set, so exit
  98.     ret            ; nope, it isn't, Real Mode is good!
  99. not_real_mode:
  100.     mov    dx,offset nrme    ; exit with msg
  101.     jmp    err16exit
  102. endp    check_mode
  103.  
  104. ;----------------------------------------------------------------------------
  105. ; this procedure just writes a zero-terminated message to the screen
  106. ; format: word x, word y, attribute byte, string, 0
  107. ; In:    DS:SI - pointer to format string
  108.  
  109. proc    write_msg
  110.     push    ax si di es
  111.     mov    ax,0b800h        ; segment of text screen
  112.     mov    es,ax            ; get it to ES
  113.     mov    ax,[si+2]        ; get Y position
  114.     mov    di,160
  115.     mul    di
  116.     add    ax,[si]
  117.     mov    di,ax
  118.     mov    ah,[si+4]        ; get attribute byte
  119.     add    si,5
  120. write_loop_pm:
  121.     mov    al,[si]
  122.     or    al,al            ; end of string?
  123.     jz    loop_end_pm
  124.     inc    si
  125.     mov    [es:di],ax
  126.     inc    di
  127.     inc    di
  128.     jmp    write_loop_pm
  129. loop_end_pm:
  130.     pop    es di si ax
  131.     ret
  132. endp    write_msg
  133.  
  134. ;----------------------------------------------------------------------------
  135. ; main procedure, this is the entry point
  136.  
  137. rm_msg        db    0,0,0,0,1fh,'Now in Real Mode - press a key to switch '
  138.         db    'to Protected Mode!',0
  139. rm2_msg        db    0,0,3,0,1fh,'Back in Real Mode - press a key to return '
  140.         db    'to DOS!',0
  141.  
  142. start16:
  143.     mov    ax,cs        ; load code-segment into DS and ES
  144.     mov    ds,ax
  145.     mov    es,ax
  146.     cli            ; better disable interrupts while setting
  147.     mov    ss,ax        ; SS and SP
  148.     mov    sp,offset stack16_end
  149.     sti            ; now interrupts don't disturb any more
  150.  
  151.     call    check_processor    ; check if we are running on at least a 80386
  152.     call    check_mode    ; check if we are running in Real Mode
  153.  
  154.     mov    ax,code16    ; get code segment into AX
  155.     movzx    eax,ax        ; clear high word
  156.     shl    eax,4        ; make a physical address
  157.     mov    [ds:code16_descriptor.base_addr0_15],ax ; store it in the dscr
  158.     mov    [ds:data16_descriptor.base_addr0_15],ax
  159.     shr    eax,8
  160.     mov    [ds:code16_descriptor.base_addr16_23],ah
  161.     mov    [ds:data16_descriptor.base_addr16_23],ah
  162.     
  163.     mov    ax,code32    ; get 32-bit code segment into AX
  164.     movzx    eax,ax        ; clear high word
  165.     shl    eax,4        ; make a physical address
  166.     mov    [ds:code32_descriptor.base_addr0_15],ax ; store it in the dscr
  167.     mov    [ds:data32_descriptor.base_addr0_15],ax
  168.     shr    eax,8
  169.     mov    [ds:code32_descriptor.base_addr16_23],ah
  170.     mov    [ds:data32_descriptor.base_addr16_23],ah
  171.  
  172.     mov    ax,code32    ; get 32-bit code segment into AX
  173.     movzx    eax,ax        ; clear high word
  174.     shl    eax,4        ; make a physical address
  175.     add    eax,offset dummy_descriptor ; calculate physical address of GDT
  176.     mov    [dword ds:gdt_start+2],eax
  177.  
  178.     mov    ax,code32    ; get 32-bit code segment into AX
  179.     movzx    eax,ax        ; clear high word
  180.     shl    eax,4        ; make a physical address
  181.     add    eax,offset interrupt_0    ; calculate physical address of IDT
  182.     mov    [dword ds:idt_start+2],eax
  183.  
  184.     mov    ax,3        ; set text mode 3, just used to clear screen
  185.     int    10h        ; do it
  186.  
  187.     mov    si,offset rm_msg; write real mode message to screen
  188.     call    write_msg
  189.  
  190.     xor    ah,ah
  191.     int    16h
  192.  
  193.     cli            ; disable interrupts
  194.     lgdt    [fword ds:global_descriptor_table]    ; load GDT register
  195.     lidt    [fword ds:interrupt_descriptor_table]    ; load IDT register
  196.     mov    eax,cr0        ; get CR0 into EAX
  197.     or    al,1        ; set Protected Mode bit
  198.     mov    cr0,eax        ; after this we are in Protected Mode!
  199.     db    0eah        ; opcode for far jump (to set CS correctly)
  200.     dw    offset start32,code32_idx
  201.  
  202. exit16:                ; the protected mode code returns here
  203.     mov    eax,cr0        ; get CR0 into EAX
  204.     and    al,not 1    ; clear Protected Mode bit
  205.     mov    cr0,eax        ; after this we are back in Real Mode!
  206.     db    0eah
  207.     dw    offset flush_ipq,code16
  208. flush_ipq:
  209.     mov    ax,cs        ; restore important registers
  210.     mov    ss,ax
  211.     mov    sp,offset stack16_end
  212.     mov    ds,ax
  213.     mov    es,ax
  214.     lidt    [fword idt_real]
  215.     sti            ; enable interrupts
  216.  
  217.     mov    si,offset rm2_msg    ; write second message
  218.     call    write_msg
  219.  
  220.     xor    ah,ah        ; wait for a key
  221.     int    16h
  222.  
  223.     mov    ax,3        ; clear screen once again
  224.     int    10h
  225.  
  226.     mov    ax,4c00h    ; everything is okay, we exit with exit-code 0
  227.     int    21h        ; bye...
  228.  
  229. ;----------------------------------------------------------------------------
  230.  
  231. ends    code16
  232.  
  233. segment code32 para public use32    ; this segment contains all 32-bit
  234. assume cs:code32, ds:code32        ; code and data stuff
  235.  
  236. stack32        db    STACK32_SIZE dup (?)    ; 32-bit stack
  237. label    stack32_end    dword
  238.  
  239. ;----------------------------------------------------------------------------
  240.  
  241. label global_descriptor_table fword    ; here begins the GDT
  242.  
  243. gdt_start      dw             gdt_size,0,0         ; val for GDT reg
  244. dummy_descriptor  segment_descriptor <0,0,0,0,0,0>
  245. code32_descriptor segment_descriptor <0ffffh,0,0,9ah,0cfh,0> ; 4GB 32-bit code
  246. data32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit data
  247. core32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit core
  248. code16_descriptor segment_descriptor <0ffffh,0,0,9ah,0,0>    ; 64k 16-bit code
  249. data16_descriptor segment_descriptor <0ffffh,0,0,92h,0,0>    ; 64k 16-bit data
  250.  
  251. gdt_size=$-(offset dummy_descriptor)
  252.  
  253. code32_idx    =    08h        ; offset of 32-bit code segment in GDT
  254. data32_idx    =    10h        ; offset of 32-bit data segment in GDT
  255. core32_idx    =    18h        ; offset of 32-bit core segment in GDT
  256. code16_idx    =    20h        ; offset of 16-bit code segment in GDT
  257. data16_idx    =    28h        ; offset of 16-bit data segment in GDT
  258.  
  259. label interrupt_descriptor_table fword    ; here begins the IDT
  260.  
  261. idt_start    dw            idt_size,0,0
  262. interrupt_0    interrupt_descriptor    <demo_int,code32_idx,0,8eh,0>
  263.  
  264. idt_size=$-(offset interrupt_0)
  265.  
  266. ;----------------------------------------------------------------------------
  267.  
  268. start32:        ; here we start in Protected Mode
  269.     mov    ax,data32_idx        ; load needed registers with the appr.
  270.     mov    ss,ax            ; selectors
  271.     mov    esp,offset stack32_end    ; stack size
  272.     mov    ds,ax
  273.     mov    es,ax
  274.     mov    fs,ax
  275.     mov    gs,ax
  276.  
  277.     call    main            ; now, everything is set up: call main!
  278.  
  279.     db    0eah    ; far jump opcode    ; when main returns, get back
  280.     dw    offset exit16,0,code16_idx    ; to the Real Mode code
  281.  
  282. ;----------------------------------------------------------------------------
  283. ; protected mode translation of write_msg
  284. ; In:    DS:ESI - pointer to format string
  285.  
  286. proc    write_msg_pm
  287.     push    ax esi edi es
  288.     mov    ax,core32_idx        ; in protected mode, we have to use
  289.                     ; core memory to address the screen
  290.     mov    es,ax
  291.     movzx    di,[esi+2]        ; get Y position
  292.     imul    edi,160
  293.     add    di,[esi]        ; add X position
  294.     add    di,[esi]
  295.     add    edi,0b8000h        ; physical address of text screen
  296.     mov    ah,[esi+4]        ; get attribute byte
  297.     add    esi,5
  298. write_loop:
  299.     mov    al,[esi]
  300.     or    al,al            ; end of string?
  301.     jz    loop_end
  302.     inc    esi
  303.     mov    [es:edi],ax
  304.     inc    edi
  305.     inc    edi
  306.     jmp    write_loop
  307. loop_end:
  308.     pop    es edi esi ax
  309.     ret
  310. endp    write_msg_pm
  311.  
  312. ;----------------------------------------------------------------------------
  313. ; sample interrupt handler
  314.  
  315. int_msg        db    0,0,2,0,1fh,'I''m the Interrupt Handler - returning '
  316.         db    'now!',0
  317.  
  318. proc    demo_int
  319.     mov    esi,offset int_msg
  320.     call    write_msg_pm
  321.     iretd
  322. endp    demo_int
  323.  
  324. ;----------------------------------------------------------------------------
  325. ; main procedure for protected mode
  326.  
  327. pm_msg        db    0,0,1,0,1fh,'Now in Protected Mode - calling Interrupt '
  328.         db    'Handler!',0
  329.  
  330. main:
  331.     mov    esi,offset pm_msg    ; just put the message...
  332.     call    write_msg_pm
  333.     int    0            ; ...call a sample interrupt...
  334.     ret                ; ...and return
  335.  
  336. ;----------------------------------------------------------------------------
  337.  
  338. ends    code32
  339.  
  340. ;****************************************************************************
  341.  
  342. end start16
  343.