| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| This is probably a perennial question but please bear with me. I am writing a small toy "OS" which I want to run in protected mode but initially switch back to real mode to use the BIOS for I/O, until I can write some device drivers. I have written a boot loader that loads a test program to 3000H:0 and executes it. However, the machine resets itself when it hits the part to switch into protected mode. Commenting out the switch to protected mode and back and it executes fine. Can anyone tell me what the problem is? I suspect that the global descriptor table is wrong, or that the global descriptor table is getting filled with crap, but I can't tell. (I'm booting a test PC from a memory stick containing the boot loader and the test program, which makes debugging tricky.) Program follows. Thanks for your patience. ; This is loaded to segment 3000H by the bootstrap loader. ; KBD_CTRL EQU 0060H ; 8042 keyboard controller control port KBD_STATUS EQU 0064H ; 8042 keyboard controller status port GDT_RELOC EQU 0600H ; base of GDT MON_STACK_TOP EQU 0FFEH ; top of monitor stack [ORG 0H] PUSH cs POP ax MOV ds, ax XOR ax, ax MOV es, ax MOV ss, ax MOV sp, MON_STACK_TOP ; Print a welcome message. MOV si, M_BANNER CALL Puts ; Relocate the Interrupt Descriptor Table and Global Descriptor Table from ; our segment into the bottom segment. ; CLD ; want things counting UP MOV si, GDT_BASE ; interrupt descriptor table MOV di, GDT_RELOC MOV cx, (GDT_END - GDT_BASE) >> 2 REP MOVSD ; Set up for protected mode (segmented memory model). ; Global Descriptor Table is set up with ; one code segment and one data segment, overlapping. ; CALL EnableA20 ; Enable A20 address line. CLI ; Let's hope an NMI doesn't happen while we're pratting around. ; Don't know whether it's used on the IBM PC, nor how to disable ; it in hardware. ; ; Load the GDT descriptor LGDT [DS:GDT_DESCRIPTOR] ; ; Enter protected mode by setting the PE bit in CR0. MOV eax, cr0 OR eax, 1 MOV cr0, eax ; ; Far jump to set CS to point to the correct segment. ; Note that segment references are now offsets in the Global Descriptor Table. ; The code segment is therefore 08H (after the NULL segment). JMP 08H:START_PROT [BITS 32] ; From here we're in protected mode. START_PROT: MOV ax, 10H ; Save data segment identifier (offset of code segment in GDT). MOV ds, ax ; Initialise a valid data segment MOV es, ax MOV fs, ax MOV gs, ax MOV ss, ax ; and stack segment. MOV esp, 090000H ; Set stack pointer. MOV BYTE[0B8000H], 'Z' ; Put something on the screen. MOV BYTE[0B8001H], 1BH ; Go back to real mode. ; ; Clear the PE bit. MOV eax, cr0 AND eax, 0FFFFFFFEH MOV cr0, eax ; ; Reset CS to real mode value by a far jump. JMP 3000H:START_REAL [BITS 16] START_REAL: PUSH cs POP ax MOV ds, ax XOR ax, ax MOV ss, ax MOV sp, MON_STACK_TOP CALL DisableA20 STI ; Print a welcome message. MOV si, M_BANNER CALL Puts HANG: JMP HANG ; ------------------------------------------------------------------------ ; A20 control routines. ; EnableA20: CALL Wait8042 MOV al, 0D1H ; write command OUT KBD_STATUS, al CALL Wait8042 MOV al, 0DFH ; enable bit 1 (A20) OUT KBD_CTRL, al CALL Wait8042 RET DisableA20: CALL Wait8042 MOV al, 0D1H ; write command OUT KBD_STATUS, al CALL Wait8042 MOV al, 0DDH ; disable bit 1 (A20) OUT KBD_CTRL, al CALL Wait8042 RET Wait8042: ; wait for the keyboard controller to finish handling input ;CALL Delay IN al, KBD_STATUS TEST al, 2 JNZ Wait8042 RET ; Put string ; Puts: LODSB OR al, al JZ .done MOV ah, 0EH MOV bx, 7 INT 10H JMP Puts ..done: RET M_BANNER: DB 'Wallo, chaps!', 13, 10, 0 .ALIGN32 ; Interrupt Descriptor Table IDT_BASE: TIMES 256 DB 0 ; Global Descriptor Table. ; Must be contiguous with IDT for relocation purposes. GDT_BASE: ; base of table ; null segment - must be present DW 0, 0, 0, 0 ; code segment: base address 0, 4GB limit, read-only ; kernel privilege DW 0FFFFh DW 0 DW 9a00H DW 00CFH ; DQ 00CF9A000000FFFFH ; data segment: base address 0, 4GB limit, read-write DW 0FFFFH DW 0 DW 9200H DW 00CFH ; DQ 00CF92000000FFFFH GDT_END: ; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT. GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes DD GDT_RELOC ; base of the GDT ; end |
|
#2
| |||
| |||
| On Aug 3, 7:02 pm, Mark Brown <spamt...@crayne.org> wrote: > This is probably a perennial question but please bear with me. > > I am writing a small toy "OS" which I want to run in protected mode > but initially switch back to real mode to use the BIOS for I/O, until > I can write some device drivers. > > I have written a boot loader that loads a test program to 3000H:0 and > executes it. However, the machine resets itself when it hits the part > to switch into protected mode. Commenting out the switch to protected > mode and back and it executes fine. > > Can anyone tell me what the problem is? I suspect that the global > descriptor table is wrong, or that the global descriptor table is > getting filled with crap, but I can't tell. (I'm booting a test PC > from a memory stick containing the boot loader and the test program, > which makes debugging tricky.) > > Program follows. Thanks for your patience. > > ; This is loaded to segment 3000H by the bootstrap loader. > ; > > KBD_CTRL EQU 0060H ; 8042 keyboard controller control port > KBD_STATUS EQU 0064H ; 8042 keyboard controller status port > GDT_RELOC EQU 0600H ; base of GDT > MON_STACK_TOP EQU 0FFEH ; top of monitor stack > > [ORG 0H] > PUSH cs > POP ax > MOV ds, ax > XOR ax, ax > MOV es, ax > MOV ss, ax > MOV sp, MON_STACK_TOP > > ; Print a welcome message. > MOV si, M_BANNER > CALL Puts > > ; Relocate the Interrupt Descriptor Table and Global Descriptor Table > from > ; our segment into the bottom segment. > ; > CLD ; want things counting UP > MOV si, GDT_BASE ; interrupt descriptor table > MOV di, GDT_RELOC > MOV cx, (GDT_END - GDT_BASE) >> 2 > REP MOVSD > > ; Set up for protected mode (segmented memory model). > ; Global Descriptor Table is set up with > ; one code segment and one data segment, overlapping. > ; > CALL EnableA20 ; Enable A20 address line. > CLI ; Let's hope an NMI doesn't happen while we're pratting > around. > ; Don't know whether it's used on the IBM PC, nor how to disable > ; it in hardware. > ; > ; Load the GDT descriptor > LGDT [DS:GDT_DESCRIPTOR] > ; > ; Enter protected mode by setting the PE bit in CR0. > MOV eax, cr0 > OR eax, 1 > MOV cr0, eax > ; > ; Far jump to set CS to point to the correct segment. > ; Note that segment references are now offsets in the Global > Descriptor Table. > ; The code segment is therefore 08H (after the NULL segment). > JMP 08H:START_PROT > > [BITS 32] > ; From here we're in protected mode. > START_PROT: > MOV ax, 10H ; Save data segment identifier (offset of code segment > in GDT). > MOV ds, ax ; Initialise a valid data segment > MOV es, ax > MOV fs, ax > MOV gs, ax > MOV ss, ax ; and stack segment. > MOV esp, 090000H ; Set stack pointer. > MOV BYTE[0B8000H], 'Z' ; Put something on the screen. > MOV BYTE[0B8001H], 1BH > > ; Go back to real mode. > ; > ; Clear the PE bit. > MOV eax, cr0 > AND eax, 0FFFFFFFEH > MOV cr0, eax > ; > ; Reset CS to real mode value by a far jump. > JMP 3000H:START_REAL > > [BITS 16] > START_REAL: > PUSH cs > POP ax > MOV ds, ax > XOR ax, ax > MOV ss, ax > MOV sp, MON_STACK_TOP > CALL DisableA20 > > STI > > ; Print a welcome message. > MOV si, M_BANNER > CALL Puts > HANG: JMP HANG > > ; > ------------------------------------------------------------------------ > > ; A20 control routines. > ; > EnableA20: > CALL Wait8042 > MOV al, 0D1H ; write command > OUT KBD_STATUS, al > CALL Wait8042 > MOV al, 0DFH ; enable bit 1 (A20) > OUT KBD_CTRL, al > CALL Wait8042 > RET > > DisableA20: > CALL Wait8042 > MOV al, 0D1H ; write command > OUT KBD_STATUS, al > CALL Wait8042 > MOV al, 0DDH ; disable bit 1 (A20) > OUT KBD_CTRL, al > CALL Wait8042 > RET > > Wait8042: ; wait for the keyboard controller to finish handling > input > ;CALL Delay > IN al, KBD_STATUS > TEST al, 2 > JNZ Wait8042 > RET > > ; Put string > ; > Puts: LODSB > OR al, al > JZ .done > MOV ah, 0EH > MOV bx, 7 > INT 10H > JMP Puts > .done: RET > > M_BANNER: DB 'Wallo, chaps!', 13, 10, 0 > > .ALIGN32 > > ; Interrupt Descriptor Table > IDT_BASE: TIMES 256 DB 0 > > ; Global Descriptor Table. > ; Must be contiguous with IDT for relocation purposes. > GDT_BASE: ; base of table > ; null segment - must be present > DW 0, 0, 0, 0 > ; code segment: base address 0, 4GB limit, read-only > ; kernel privilege > DW 0FFFFh > DW 0 > DW 9a00H > DW 00CFH > ; DQ 00CF9A000000FFFFH > ; data segment: base address 0, 4GB limit, read-write > DW 0FFFFH > DW 0 > DW 9200H > DW 00CFH > ; DQ 00CF92000000FFFFH > GDT_END: > > ; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT. > GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes > DD GDT_RELOC ; base of the GDT > > ; end I can immediately see a few problems: 1. the instructions in the code are compiled with the assumption of the segment base = 3000H * 16, where (E)IP is 0. However your GDT describes the segments as having base = 0. Guess to what physical location JMP 08H:START_PROT is jumping? You either need to redo the GDT (change or add more segments) or change the initial instructions in the above code and introduce ORG START_PROT + 3000H * 16 right after START_PROT and also move all(!) real mode code before START_PROT... Actually, in the latter case you need to carefully separate all 16-bit and 32-bit code (and data used by them) or you'll have the same problem as now but in a different place. Don't forget that the compiled x86 code is generally not position- independent and therefore its your responsibility to take care of all segment bases and offsets (know what, where and when). 2. Clearing CR0.PE doesn't switch to a 16-bit mode (not supposed to just like setting CR0.PE). Nor does JMP 3000H:START_REAL. The 16- bit mode must be set prior to clearing CR0.PE. 3. Before clearing CR0.PE all segments must be loaded with selectors of segments that are setup appropriately for operation in real mode (effective limit (>= 65535), type (present, DPL=0, not expand-down, not conforming, 16-bit for CS, not big for SS). I'd also advise to comment out the A20 manipulation code for now. If there's a bug in it, it can also cause a reset (in the same port there's a bit setting (or is it resetting?) which will reset the CPU). Alex |
|
#3
| |||
| |||
| Am Sun, 3 Aug 2008 19:02:10 -0700 (PDT) schrieb Mark Brown: > CLI ; Let's hope an NMI doesn't happen while we're pratting > around. > ; Don't know whether it's used on the IBM PC, nor how to disable > ; it in hardware. RBIL->inter61d.zip->Ports.a ----------P0070007F-------------------------- PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) Note: the real-time clock may be either a discrete MC146814, MC146818, or an emulation thereof built into the motherboard chipset SeeAlso: PORT 00A0h"XT" 0070 -W CMOS RAM index register port (ISA, EISA) bit 7 = 1 NMI disabled from reaching CPU = 0 NMI enabled Ralf Browns Interrupt List(RBIL): http://www.pobox.com/~ralf http://www.pobox.com/~ralf/files.html ftp://ftp.cs.cmu.edu/afs/cs.cmu.edu/user/ralf/pub/ Dirk |
|
#4
| |||
| |||
| "Mark Brown" <spamtrap@crayne.org> wrote in message news:f53f616f-ecf0-4b9f-b75e-85eb612035f6@p31g2000prf.googlegroups.com... > This is probably a perennial question but please bear with me. > > I am writing a small toy "OS" which I want to run in protected mode > but initially switch back to real mode to use the BIOS for I/O, until > I can write some device drivers. > > I have written a boot loader that loads a test program to 3000H:0 and > executes it. However, the machine resets itself when it hits the part > to switch into protected mode. Commenting out the switch to protected > mode and back and it executes fine. > > Can anyone tell me what the problem is? I suspect that the global > descriptor table is wrong, or that the global descriptor table is > getting filled with crap, but I can't tell. (I'm booting a test PC > from a memory stick containing the boot loader and the test program, > which makes debugging tricky.) > > Program follows. Thanks for your patience. > > > ; This is loaded to segment 3000H by the bootstrap loader. > ; > > KBD_CTRL EQU 0060H ; 8042 keyboard controller control port > KBD_STATUS EQU 0064H ; 8042 keyboard controller status port > GDT_RELOC EQU 0600H ; base of GDT > MON_STACK_TOP EQU 0FFEH ; top of monitor stack > > > [ORG 0H] > PUSH cs > POP ax > MOV ds, ax > XOR ax, ax > MOV es, ax > MOV ss, ax > MOV sp, MON_STACK_TOP > > ; Print a welcome message. > MOV si, M_BANNER > CALL Puts > > ; Relocate the Interrupt Descriptor Table and Global Descriptor Table > from > ; our segment into the bottom segment. > ; > CLD ; want things counting UP > MOV si, GDT_BASE ; interrupt descriptor table > MOV di, GDT_RELOC > MOV cx, (GDT_END - GDT_BASE) >> 2 > REP MOVSD > 1) Have you tried to see if it works without relocating the GDT? 2) And, have you confirmed that you don't have an off by one error with 'cx'? > ; Set up for protected mode (segmented memory model). > ; Global Descriptor Table is set up with > ; one code segment and one data segment, overlapping. > ; > CALL EnableA20 ; Enable A20 address line. I would probably comment out the A20 and GDT relocation until I found the reset problem. > CLI ; Let's hope an NMI doesn't happen while we're pratting > around. > ; Don't know whether it's used on the IBM PC, nor how to disable > ; it in hardware. > ; > ; Load the GDT descriptor > LGDT [DS:GDT_DESCRIPTOR] > ; > ; Enter protected mode by setting the PE bit in CR0. > MOV eax, cr0 > OR eax, 1 > MOV cr0, eax > ; > ; Far jump to set CS to point to the correct segment. > ; Note that segment references are now offsets in the Global > Descriptor Table. > ; The code segment is therefore 08H (after the NULL segment). If you place a "JMP $" here, does it hang instead of reboot? If yes, good until here. > JMP 08H:START_PROT > > [BITS 32] > ; From here we're in protected mode. > START_PROT: If you place a "JMP $" here, does it hang instead of reboot? If yes, good until here. If yes to other "JMP $", and no to this one, then there is a problem with your selector, descriptor, or LGDT. Which is why I suggested you try to get it working without relocating the GDT. > MOV ax, 10H ; Save data segment identifier (offset of code segment > in GDT). > MOV ds, ax ; Initialise a valid data segment > MOV es, ax > MOV fs, ax > MOV gs, ax > MOV ss, ax ; and stack segment. > MOV esp, 090000H ; Set stack pointer. > MOV BYTE[0B8000H], 'Z' ; Put something on the screen. > MOV BYTE[0B8001H], 1BH > If you place a "JMP $" here, does it hang instead of reboot? If yes, good until here. > ; Go back to real mode. > ; > ; Clear the PE bit. > MOV eax, cr0 > AND eax, 0FFFFFFFEH (save a byte or few, AND with 0xFE sign extended to 0xFFFFFFFE. E.g., for NASM: AND eax, byte -0x02) > MOV cr0, eax > ; > ; Reset CS to real mode value by a far jump. > JMP 3000H:START_REAL I'm not sure this is correct. Someone else will need to clarify, but I thought you needed to do PM far jump to a PM selector setup to match RM address segments, and then jump again via a RM far jump to go to RM... I.e., you need two more selectors (code,data) in your GDT for RM, don't you? > > [BITS 16] > START_REAL: > PUSH cs > POP ax > MOV ds, ax > XOR ax, ax > MOV ss, ax > MOV sp, MON_STACK_TOP > CALL DisableA20 > > STI > > ; Print a welcome message. > MOV si, M_BANNER > CALL Puts > HANG: JMP HANG > > > ; > ------------------------------------------------------------------------ > > ; A20 control routines. > ; > EnableA20: > CALL Wait8042 > MOV al, 0D1H ; write command > OUT KBD_STATUS, al > CALL Wait8042 > MOV al, 0DFH ; enable bit 1 (A20) > OUT KBD_CTRL, al > CALL Wait8042 > RET > > DisableA20: > CALL Wait8042 > MOV al, 0D1H ; write command > OUT KBD_STATUS, al > CALL Wait8042 > MOV al, 0DDH ; disable bit 1 (A20) > OUT KBD_CTRL, al > CALL Wait8042 > RET > > Wait8042: ; wait for the keyboard controller to finish handling > input > ;CALL Delay > IN al, KBD_STATUS > TEST al, 2 > JNZ Wait8042 > RET > > > ; Put string > ; > Puts: LODSB > OR al, al > JZ .done > MOV ah, 0EH > MOV bx, 7 > INT 10H > JMP Puts > .done: RET > > M_BANNER: DB 'Wallo, chaps!', 13, 10, 0 > > > .ALIGN32 > > ; Interrupt Descriptor Table > IDT_BASE: TIMES 256 DB 0 > > ; Global Descriptor Table. > ; Must be contiguous with IDT for relocation purposes. > GDT_BASE: ; base of table > ; null segment - must be present > DW 0, 0, 0, 0 > ; code segment: base address 0, 4GB limit, read-only > ; kernel privilege > DW 0FFFFh > DW 0 > DW 9a00H > DW 00CFH > ; DQ 00CF9A000000FFFFH > ; data segment: base address 0, 4GB limit, read-write > DW 0FFFFH > DW 0 > DW 9200H > DW 00CFH > ; DQ 00CF92000000FFFFH > GDT_END: > > ; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT. > GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes Off by one? DW GDT_END - GDT_BASE -1 ; > DD GDT_RELOC ; base of the GDT > > > ; end > Just a quick skim, I'm not sure any was your issue... Rod Pemberton |
|
#5
| |||
| |||
| On 4 Aug, 03:02, Mark Brown <spamt...@crayne.org> wrote: > This is probably a perennial question but please bear with me. > > I am writing a small toy "OS" which I want to run in protected mode > but initially switch back to real mode to use the BIOS for I/O, until > I can write some device drivers. > > I have written a boot loader that loads a test program to 3000H:0 and > executes it. However, the machine resets itself when it hits the part > to switch into protected mode. Commenting out the switch to protected > mode and back and it executes fine. > > Can anyone tell me what the problem is? I suspect that the global > descriptor table is wrong, or that the global descriptor table is > getting filled with crap, but I can't tell. (I'm booting a test PC > from a memory stick containing the boot loader and the test program, > which makes debugging tricky.) > > Program follows. Thanks for your patience. <code snipped> It took me a while to get the PM transition right. Like you every time I ran the code it reset the machine because something wasn't right. My last find was that it wasn't enough to identity-map the code (in my case code is in range 0x0001_0000 to 0x0001_ffff was only mapped as part). I had to have a 64k code segment for exactly this address range. Below is some minimalistic code that transfers to PM bearing this in mind. Maybe it will help. By the way, booting always on real hardware is very time consuming. You may want to try some virtualisation software. I found Vmware Server (free) and Microsoft Virtual PC (also free) worked well. With these, and using a floppy to boot, to test a new version of code all you need to do is compile, copy to floppy and reboot the virtual machine. You could maybe do similar with USB flash rather than floppy if you prefer that. ;************************************************* ********************* ;* ;* 386 code from here though we are still running 16-bit real mode ;* ;************************************************* ********************* cpu 386 bits 16 ; ;We can now use 32-bit registers for values but addresses need to ;remain within a 64k offset from a segment register. ; call initial_enable_a20 ;First attempt at enabling A20 call get_memory_map ;Get standard form memory map call set_up_gdt_32r call nmi_disable ;Prevent NMIs call a20_required_32r ;Ensure A20 enabled or abort cli ;Prevent other interrupts lgdt [gdt.limit] mov eax, cr0 ;Change or eax, IA32_CR0_PE ; to mov cr0, eax ; protected jmp 32 mode_start ; modecpu 386 bits 32 pmode_start: ;************************************************* ********************* ;* ;* Protected mode from here ;* ;************************************************* ********************* The GDT pointer referred to above is defined as align 8 gdt: dw 0 ..limit dw 256 * 8 - 1 ;Initial GDT limit ..base dd 0x8000 ; and address The GDT contents are set up with ;************************************************* ********************* ;* ;* Set up initial GDT ;* ;************************************************* ********************* set_up_gdt_32r: push eax push ecx push edx push es push di mov di, [gdt.base] xor eax, eax mov es, ax mov cx, 256 ..clear_gdt: mov [es:di], eax mov [es:di + 4], eax add di, 8 loop .clear_gdt ;Set up a data descriptor for the entire address space mov di, [gdt.base] add di, 8 ;GDT.(1) xor edx, edx ;Base = 0 mov ecx, -1 ;Limit = max mov bh, 0x80 + 0x40 ;G + D/B mov bl, 0x80 + 0x10 + 2 ;P + S + type 2 (r/w data) call descriptor_32r.create ;Write the values ;Set up a code descriptor for the entire address space mov di, [gdt.base] add di, 16 ;GDT.(2) xor edx, edx ;Base = 0 mov ecx, -1 ;Limit = max mov bh, 0x80 + 0x40 ;G + D/B mov bl, 0x80 + 0x10 + 8 ;P + S + type 8 (exe only) call descriptor_32r.create ;Write the values ;Set up a descriptor for the data in this code mov di, [gdt.base] add di, 24 ;GDT.(3) mov edx, 0x10000 ;Base = 0x0001_0000 mov ecx, 65535 ;Limit mov bh, 0x40 ;D/B mov bl, 0x80 + 0x10 + 2 ;P + S + type 2 (r/w data) call descriptor_32r.create ;Write the values ;Set up a descriptor for this code mov di, [gdt.base] add di, 32 ;GDT.(4) mov edx, 0x10000 ;Base = 0x0001_0000 mov ecx, 65535 ;Limit mov bh, 0x40 ;D/B mov bl, 0x80 + 0x10 + 10 ;P + S + type 10 (exe, read) call descriptor_32r.create ;Write the values pop di pop es pop edx pop ecx pop eax ret ;************************************************* ********************* ;* ;* Descriptor maintenance ;* ;************************************************* ********************* %include "ia32_defs.inc" descriptor_32r: ;All these use es:di pointing at the descriptor address ..create: ;Enter with ; ; edx = base address ; ecx = limit in bytes (even where granularity bit set) ; ebx ; bh = g, d/b, l, avl in top 4 bits ; bl = p, dpl, s, type ; push ebx push ecx push edx ;Set the base from edx mov [es:di + descript.base_15_0], dx ;Base bytes 1 and 0 shr edx, 16 mov [es:di + descript.base_23_16], dl ;Base byte 2 mov [es:di + descript.base_31_24], dh ;Base byte 3 ;Set the type byte - P, DPL, S, Type mov [es:di + descript.pddstttt], bl ;Set type byte flags test bh, 0x80 ;Granularity bit set? jz .set_limit ;Skip if no shr ecx, 12 ;Div 32-bit by 4096 ..set_limit: and bh, 0xf0 ;Mask out limit bits mov [es:di + descript.lim_15_0], cx ;Limit bits 15 :- 0 shr ecx, 16 and cl, 0xf ;Limit bits 19 :- 16 or cl, bh ;Merge g, d/b, l, avl mov [es:di + descript.gdlallll], cl ;Set these 8 bits pop edx pop ecx pop ebx ret |
|
#6
| |||
| |||
| "ark Brown asked: {about returning to RM16] the manuals (Intel and AMD) states that it is required to switch from PM32 to RM16 by using an intermediate PM16 state: :use32 jmp far PM16 PM16: :use16 .... ;restore RM16 segment limits or keep it 'Unreal' jmp far RM16 RM16: .... __ wolfgang I checked it as not required on a few 486-CPUs, but who will rely on the undocumented ...? btw: I missed a check for A20 is actually enabled in your code |
|
#7
| |||
| |||
| I got it working finally, at least as far as the switch to PM. The switch back to RM is the next challenge. Many thanks to those who took the time to respond. I'm very grateful and it probably saved me a week ;-) The main problem was highlighted by Alexei - the segments were all over the place. I hadn't realised the far jump didn't take a 32-bit address as an argument, but only a 16-bit address referenced to a base pointer. (I come from a 68000 assembler background, and before that Z80, so this segmentation stuff is totally alien and is causing no end of grief.) So, I re-wrote the code to relocate itself. The extract of the pertinent section is as follows. ; This is loaded to segment 3000H by the bootstrap loader. ; ORIGSEG EQU 3000H IDT_RELOC EQU 0500H ; base of IVT (256 bytes) GDT_RELOC EQU 0600H ; base of GDT MON_RELOC EQU 0700H ; base of relocated monitor program MON_STACK_TOP EQU 0FFEH ; top of (16-bit) monitor stack [BITS 16] [ORG ORIGSEG*16] RELOC_START: ; Relocate from here. CLI PUSH cs POP ax MOV ds, ax XOR ax, ax ; initialise stack pointer MOV ss, ax ; lives in the bottom segment MOV sp, MON_STACK_TOP MOV es, ax ; es also points to bottom segment STI ; Print a welcome message. MOV si, M_BANNER CALL Puts ; Relocate the IDT and GDT. ; CLD ; want things counting UP MOV si, IDT_BASE ; interrupt descriptor table MOV di, IDT_RELOC MOV cx, (GDT_END - GDT_BASE + 256) >> 2 REP MOVSD ; Relocate code from RELOC_START through RELOC_END. ; MOV si, RELOC_START MOV di, MON_RELOC MOV cx, (RELOC_END - RELOC_START) >> 2 REP MOVSD ; Restart at the relocated code. ; JMP MON_RELOC>>4:RELOC_GO ; Come here after relocation. RELOC_GO: PUSH cs ; load new data segment base POP ax MOV ds, ax ; Set up for protected mode (segmented, pseudo-flat memory ; model). GDT is set up with one code segment and one data ; segment, overlapping. ; ; CALL EnableA20 ; Enable A20 address line. ; CALL InhibitNMI ; Inhibit NMI (in hardware!) CLI ; ; Load the GDT descriptor LGDT [GDT_DESCRIPTOR] ; ; Load the Interrupt Descriptor Table. ; LIDT [IDT_DESCRIPTOR] ; ; Enter protected mode by setting the PE bit in CR0. MOV eax, cr0 OR eax, 1 MOV cr0, eax ; ; Far jump to set CS to point to the correct segment. ; Note that segment references are now offsets in the GDT. ; The code segment is therefore at offset 08H ; (immediately after the NULL segment). ; JMP 08H:MON_RELOC+START_PROT [BITS 32] ; From here we're in protected mode. START_PROT: MOV ax, 10H ; Save data segment identifier (offset of code segment in GDT). MOV ds, ax ; Initialise a valid data segment MOV es, ax MOV fs, ax MOV gs, ax MOV ss, ax ; and stack segment. MOV esp, 090000H ; Set the stack pointer. MOV BYTE[0B8000H], 'Z' ; Put something on the screen. MOV BYTE[0B8001H], 1BH JMP $ ; Go back to real mode. ; Need to preload segment selectors as appropriate. ; ; Clear the PE bit. MOV eax, cr0 AND eax, 0FFFFFFFEH MOV cr0, eax ; ; Do a far jump back into the monitor code. JMP MON_RELOC>>4:START_REAL [BITS 16] START_REAL: PUSH cs POP ax MOV ds, ax XOR ax, ax MOV ss, ax MOV sp, MON_STACK_TOP ;CALL DisableA20 STI ; Print a welcome message. MOV si, M_BANNER CALL Puts HANG: JMP HANG I also tried Virtual PC and VMware server as suggested, but they don't boot off of USB, so the next job is creating a boot floppy I suppose. Thanks very much again. Mark. |
|
#8
| |||
| |||
| Mark Brown wrote: > I got it working finally, at least as far as the switch to PM. The > switch back to RM is the next challenge. You might find this info from Bob Smith helpful... http://www.sudleyplace.com/pmtorm.html Best, Frank |
|
#9
| |||
| |||
| Sorry, new problem. I manage to switch back to real mode (setting a 16- bit segment for code and data). However, after switching back to RM, even though the segment selector should be 16-bit, the CPU appears to take 32-bit operands as default, and I have to put an 0x66H before some instructions to get them to be interpreted as 16-bit. This I discovered with by using Bochs in debug mode and single- stepping the program. Any ideas? Cheers, Mark. |
|
#10
| |||
| |||
| Mark Brown wrote: > ; > ; Far jump to set CS to point to the correct segment. > ; Note that segment references are now offsets in the GDT. > ; The code segment is therefore at offset 08H > ; (immediately after the NULL segment). > ; > JMP 08H:MON_RELOC+START_PROT > You need a DWORD qualifier here: JMP 08H WORD MON_RELOC+START_PROTOtherwise your address (which will look like 0x0003XXXX) will be truncated to 16 bits. -hpa |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.