News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Protected-mode stack issues.

Started by r_miele, January 15, 2005, 04:43:33 AM

Previous topic - Next topic

r_miele

I'm having an issue with this code and I hope someone can help me with it.  The code is supposed to put the processor into protected-mode and display an "Hello World!" message with a screen border.  It does this by calling two procedures at the end of the 32-bit code segment.  I don't think I set up the 32-bit stack segment correctly because it just displays garbage.  I have it set up so that when the system is put into protected mode a 64k data segment which encompasses the VGA buffer is set to DS, ES is set to a 32-bit data segment which contains the strings, and SS is set to a 32-bit stack segment. 

On a side note, when you comment out the lines of code were SS is being assigned the descriptor value for the 32-bit stack, it displays "Hello Hello".   Any ideas?  Thanks :thumbu



.MODEL SMALL
.386p

.STACK

GDT_DESCR STRUC
gdt_size              WORD 0
gdt_location          DWORD 0
GDT_DESCR ENDS

GDT_ENTRY STRUC
segment_size15_0 WORD 0
base_addr15_0 WORD 0
base_addr23_16 BYTE 0
p_dpl_s_type BYTE 0
g_db_0_avl_seg19_16 BYTE 0
base_addr31_24 BYTE 0
GDT_ENTRY ENDS

rm_codeSel EQU 8 ; Real-Mode Code segment.
rm_dataSel EQU 16 ; Real-Mode Data segment.
pm_vidSel EQU 24 ; Video buffer segment (for display from PM).
pm_codeSel EQU 32 ; Protected-Mode Code segment.
pm_dataSel EQU 40 ; Protected-Mode Data segment.
pm_stackSel EQU 48 ; Protected-Mode Stack segment.

.DATA

gdt_descriptor GDT_DESCR  <55>

gdt GDT_ENTRY <>, \ ; "\": MASM's continuation character.
<0FFFFh, , , 09Ah, 08Fh>, \ ; 08h: 16-bit Real-Mode CS Segment.
<0FFFFh, , , 092h>, \ ; 10h: 16-bit Real-Mode DS Segment.
<0FFFFh, 08000h, 000Bh, 092h>, \ ; 18h: Video buffer for pm output.
<0FFFFh, , , 09Ah, 04Fh>, \ ; 20h: 32-bit code segment.
<0FFFFh, , , 092h, 040h>, \ ; 28h: 32-bit data segment.
<0FFFFh, , , 092h, 040h> ; 30h: 32-bit stack segment.

.CODE

MAIN PROC

start_rm:

; Set DS and ES to the data segment.
MOV AX, @DATA
MOV DS, AX
MOV ES, AX

; Set SS to the stack segment.
MOV AX, @STACK
MOV SS, AX

; Get linear address of GDT
MOV AX, DS
MOVZX EAX, AX
SHL EAX, 4
ADD EAX, OFFSET gdt
MOV gdt_descriptor.gdt_location, EAX

; Set decriptor 8h to base of CS
MOV AX, CS
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 10h to the base of DS.
MOV AX, DS
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 28h to the base of PM_DAT.
MOV AX, PM_DAT
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 30h to the base of PM_STACK.
MOV AX, PM_STACK
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set call to flat 32 bit code
MOV AX, PM_TXT
MOVZX EAX, AX
SHL EAX, 4
MOV DX, OFFSET start
MOVZX EDX, DX
ADD EAX, EDX
MOV CS:[off_32], EAX

; Initalizing the GDTR.
LGDT gdt_descriptor

; Go to PM
CLI
MOV     EAX, CR0
OR      AL, 01h
MOV     CR0, EAX

; Do intersegment jump to set CS and flush instruction queue
JMP FWORD PTR [off_32] ; jmp fword ptr 20h:start
off_32 DWORD 0
WORD pm_codeSel ; 20h

MAIN ENDP


PM_DAT SEGMENT USE32 DWORD PRIVATE 'DATA'

message1 BYTE "Hello", 0
message2 BYTE "World!!!", 0

PM_DAT ENDS


PM_STACK SEGMENT USE32 DWORD PRIVATE 'STACK'

DWORD 8192 DUP (?) ; 65536 Bytes

PM_STACK ENDS


PM_TXT SEGMENT USE32 DWORD PRIVATE 'CODE'

PM_MAIN PROC
start::
; Loading DS with the Video buffer for pm output.
MOV AX, pm_vidSel
MOV DS, AX

; Loading ES with the descriptor containing the protected-mode data segment.
MOV AX, pm_dataSel
MOV ES, AX

; Loading SS with the descriptor containing the protected-mode stack segment.
MOV AX, pm_stackSel
MOV SS, AX


screen_setup:
CALL DrawBorder

MOV CL, 2
MOV CH, 2
MOV EDX, OFFSET ES:message1
CALL WriteString

MOV CL, 2
MOV CH, 8
MOV EDX, OFFSET ES:message2
CALL WriteString

CLI
HLT

PM_MAIN ENDP

;-----------------------------------------------------------------------------------------------
;Protected-Mode Procedures
;-----------------------------------------------------------------------------------------------

; DS must be set to descriptor pm_vidSel.
DrawBorder PROC
MOV EDI, 0

MOV AX, 07C9h
    MOV [EDI], AX
INC EDI
INC EDI

MOV AX, 07CDh
MOV CX, 78
.WHILE (CX)
MOV [EDI], AX
INC EDI
INC EDI
DEC CX
.ENDW

MOV AX, 07BBh
MOV [EDI], AX
INC EDI
INC EDI

MOV BX, 10
.WHILE (BX)
MOV AX, 07BAh
MOV [EDI], AX
INC EDI
INC EDI

MOV AX, 0h
MOV CX, 78
.WHILE (CX)
MOV [EDI], AX
INC EDI
INC EDI
DEC CX
.ENDW

MOV AX, 07BAh
MOV [EDI], AX
INC EDI
INC EDI
DEC BX
.ENDW

MOV AX, 07CCh
MOV [EDI], AX
INC EDI
INC EDI

MOV AX, 07CDh
MOV CX, 78
.WHILE (CX)
MOV [EDI], AX
INC EDI
INC EDI
DEC CX
.ENDW

MOV AX, 07B9h
MOV [EDI], AX
INC EDI
INC EDI

MOV BX, 12
.WHILE (BX)
MOV AX, 07BAh
MOV [EDI], AX
INC EDI
INC EDI

MOV AX, 0h
MOV CX, 78
.WHILE (CX)
MOV [EDI], AX
INC EDI
INC EDI
DEC CX
.ENDW

MOV AX, 07BAh
MOV [EDI], AX
INC EDI
INC EDI
DEC BX
.ENDW

MOV AX, 07C8h
MOV [EDI], AX
INC EDI
INC EDI

MOV AX, 07CDh
MOV CX, 78
.WHILE (CX)
MOV [EDI], AX
INC EDI
INC EDI
DEC CX
.ENDW

MOV AX, 07BCh
MOV [EDI], AX
RET
DrawBorder ENDP   


WriteString PROC
; DS must be set to descriptor pm_vidSel.
; ES must be set to descriptor pm_dataSel.
; String offset be in EDX.
; Row must be in CL.
; Column must be in CH.

; Calculate starting point on screen of string.
MOV EDI, 0
MOVZX EAX, CL ;Row
SUB EAX, 1
MOV EBX, 80
MUL EBX
MOVZX ECX, CH
ADD EAX, ECX ;Column
MOV EBX, 2
MUL EBX
ADD EDI, EAX
L1:
MOV CL, ES:[EDX]
CMP CL, 0
JZ L2
MOV CH, 07h
MOV [EDI], CX
INC EDI
INC EDI
INC EDX
JMP L1
L2:
RET
WriteString ENDP

PM_TXT ENDS

END MAIN   



MichaelW

#1
In your startup code @STACK is returning the same value as @DATA, so you are setting both to the segment address of DGROUP, but you are not adjusting SP as the .STARTUP directive would. But I cannot see how this could be the cause of the problem, because you are not using the RM stack.

Normally, when you set SS you also set the stack pointer, and the processor is designed to facilitate this. From the Pentium Processor Family Developer's Manual, Volume 3: Architecture and Programming Manual:
Quote
A MOV into SS instruction inhibits all interrupts until after the execution of the next instruction (which should be a MOV into ESP instruction).
When you set SS to the PM stack, you should set ESP to the end of the PM stack (a stack "expands" down in memory). Again, I cannot see how this could be the cause of the problem, but I think it's at least suspect.

Here is the MAP file generated by the linker:


Start  Stop   Length Name                   Class
00000H 000B5H 000B6H _TEXT                  CODE
000B8H 00208H 00151H PM_TXT                 CODE
0020AH 00247H 0003EH _DATA                  DATA
00248H 00256H 0000FH PM_DAT                 DATA
00260H 0065FH 00400H STACK                  STACK
00660H 0865FH 08000H PM_STACK               STACK

Origin   Group
0020:0   DGROUP

  Address         Publics by Name

000B:0040       DrawBorder
0000:0000       MAIN
000B:0008       PM_MAIN
000B:0126       WriteString

  Address         Publics by Value

0000:0000       MAIN
000B:0008       PM_MAIN
000B:0040       DrawBorder
000B:0126       WriteString

Program entry point at 0000:0000

eschew obfuscation

r_miele

I implemented the changes you recommended MichaelW but it still didn't work.  Here is the code, maybe I didn't do it correctly.  The changes I made are indicated by the colored stars.


.DATA

gdt_descriptor GDT_DESCR  <55>

gdt GDT_ENTRY <>, \ ; "\": MASM's continuation character.
<0FFFFh, , , 09Ah, 08Fh>, \ ; 08h: 16-bit Real-Mode CS Segment.
<0FFFFh, , , 092h>, \ ; 10h: 16-bit Real-Mode DS Segment.
<0FFFFh, 08000h, 000Bh, 092h>, \ ; 18h: Video buffer for pm output.
<0FFFFh, , , 09Ah, 04Fh>, \ ; 20h: 32-bit code segment.
<0FFFFh, , , 092h, 040h>, \ ; 28h: 32-bit data segment.
; <0FFFFh, , , 092h, 040h> ; 30h: 32-bit stack segment. OLD STACK
<0FFFFh, , , 096h, 040h> ; NEW STACK[font=Verdana]*[/font]

sta_loc DWORD 0 ;[font=Verdana]*[/font]

.CODE

MAIN PROC

start_rm:

; Set DS and ES to the data segment.
MOV AX, @DATA
MOV DS, AX
MOV ES, AX

; Set SS to the stack segment.
MOV AX, @STACK
MOV SS, AX

; Get linear address of GDT
MOV AX, DS
MOVZX EAX, AX
SHL EAX, 4
ADD EAX, OFFSET gdt
MOV gdt_descriptor.gdt_location, EAX

; Set decriptor 8h to base of CS
MOV AX, CS
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+1*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 10h to the base of DS.
MOV AX, DS
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+2*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 28h to the base of PM_DAT.
MOV AX, PM_DAT
MOVZX EAX, AX
SHL EAX, 4
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+5*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set descriptor 30h to the base of PM_STACK.
MOV AX, PM_STACK
MOVZX EAX, AX
SHL EAX, 4
MOV sta_loc, EAX; [font=Verdana]*[/font]
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr15_0, AX
    SHR EAX, 16
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr23_16, AL
MOV [gdt+6*SIZEOF GDT_ENTRY].base_addr31_24, AH

; Set call to flat 32 bit code
MOV AX, PM_TXT
MOVZX EAX, AX
SHL EAX, 4
MOV DX, OFFSET start
MOVZX EDX, DX
ADD EAX, EDX
MOV CS:[off_32], EAX

; Initalizing the GDTR.
LGDT gdt_descriptor

; Go to PM
CLI
MOV     EAX, CR0
OR      AL, 01h
MOV     CR0, EAX

; Do intersegment jump to set CS and flush instruction queue
JMP FWORD PTR [off_32] ; jmp fword ptr 20h:start
off_32 DWORD 0
WORD pm_codeSel ; 20h

MAIN ENDP


PM_DAT SEGMENT USE32 DWORD PRIVATE 'DATA'

message1 BYTE "Hello", 0
message2 BYTE "World!!!", 0

PM_DAT ENDS


PM_STACK SEGMENT USE32 DWORD PRIVATE 'STACK'

DWORD 8192 DUP (?) ; 65536 Bytes

PM_STACK ENDS


PM_TXT SEGMENT USE32 DWORD PRIVATE 'CODE'

PM_MAIN PROC
start::
; Loading DS with the Video buffer for pm output.
MOV AX, pm_vidSel
MOV DS, AX

; Loading ES with the descriptor containing the protected-mode data segment.
MOV AX, pm_dataSel
MOV ES, AX

; Loading ES with the descriptor containing the protected-mode data segment.
MOV AX, rm_dataSel
MOV GS, AX

; Loading SS with the descriptor containing the protected-mode stack segment.
MOV AX, pm_stackSel
MOV SS, AX

MOV EAX, GS:sta_loc;[font=Verdana]*[/font]
ADD EAX, 65536;[font=Verdana]*[/font]
MOV ESP, EAX;[font=Verdana]*[/font]

screen_setup:
CALL DrawBorder

MOV CL, 2
MOV CH, 2
MOV EDX, OFFSET ES:message1
CALL WriteString

MOV CL, 2
MOV CH, 8
MOV EDX, OFFSET ES:message2
CALL WriteString

CLI
HLT

PM_MAIN ENDP


MichaelW

Your MOV SS, AX instruction should be immediately followed by a MOV ESP, 65536. You don't want to add to the stack pointer, you want to set it so it points to the end of the stack segment.

Also, the expand-down bit has nothing to do with how the stack operates (the stack pointer being decreased when values are pushed and increased when values are popped). Per Intel:
Quote
If the stack segment needs to be able to change size, it can be an expand-down segment. The meaning of the segment limit is reversed for an expand-down segment. The valid offsets in an expand-down segment are those which generate exceptions in expand-up segments. Expand-up segments must be addressed by offsets which are equal or less than the segment limit. Offsets into expand-down segments always must be greater than the segment limit. This interpretation of the segment limit causes memory space to be allocated at the bottom of the segment when the segment limit is decreased, which is correct for stack segments because they grow towards lower addresses. If the stack is given a segment which does not change size, the segment does not need to be expand-down.

eschew obfuscation

r_miele

I modified the code per your specifications MichaelW.  The program is not displaying jibberish anymore but it is displaying "Hello Hello" instead of "Hello World!".  The list file shows that the right offsets are being used.  Any other ideas?



00000000 PM_DAT SEGMENT USE32 DWORD PRIVATE 'DATA'

00000000 48 65 6C 6C 6F message1 BYTE "Hello", 0
   00
00000006 57 6F 72 6C 64 message2 BYTE "World!!!", 0
   21 21 21 00

00B3 PM_DAT ENDS



00000026  B1 02 MOV CL, 2
00000028  B5 02 MOV CH, 2
0000002A  BA 00000000 R MOV EDX, OFFSET ES:message1
0000002F  E8 000000F6 CALL WriteString

00000034  B1 02 MOV CL, 2
00000036  B5 08 MOV CH, 8
00000038  BA 00000006 R MOV EDX, OFFSET ES:message2
0000003D  E8 000000E8 CALL WriteString


MichaelW

I can see at least one error in the WriteString procedure in your original post. The offset of the string is being passed in EDX, but before it uses the value in EDX the procedure performs a multiply that overwrites the value with the most significant dword of the product. Also, you adjust the row coordinate to base 0, but not the column coordinate, and it would be easier and more efficient to use a left shift to multiply the column by two.

If I were doing this I would debug the DrawBorder and WriteString procedures from the RM DOS part of the program (because this could be done conveniently under Windows). You would need to load appropriate register values prior to the calls, and temporarily disable the PM code.
eschew obfuscation

r_miele

In the immortal words of Homer Simpson, "DOOH!!!!". 

I can't believe I made such a stupid mistake.  Thanks so much for helping me with that MichaelW, I was beginning to go crazy.   :thumbu