I am putting together an 80386 S-100 bus board and am writing a ROM based monitor to use with the board. See here:-
http://s100computers.com/My%20System%20Pages/80386%20Board/80386%20CPU%20Board.htm
After a steep learning curve using selectors etc. I managed to switch the CPU in and out of protected mode (PM) using a flat 4GB address space and 32 bit registers. It’s so nice not having to worry about segments!
However I am having a real problem getting software interrupts (have not even tried hardware ones yet). The board/software works fine in real mode for both software and 8259A driven hardware interrupts. Can run MSDOS “out of the box” for example.
I won’t use up your time on basic stuff. Here is the core issue/code. I am using the NASM assembler.
First the switch to PM mode. Works fine!
……..
o32 LGDT [dword CS:GdtDesc] ;Initilize GDTR (for GdtDesc, see below)
o32 LIDT [dword CS:IdtDesc] ;Initilize IDTR (for IdtDesc, see below)
MOV EAX,CR0 ;Set to protected mode
OR EAX,1
MOV CR0,EAX ;<---- GOTO PM MODE
JMP $+2 ;Flush
;The CPU is now executing in 16-bit
;protected mode.
;Make a far jump in order to load CS
;with a selector
;to our 32-bit executable code
;descriptor.
o32 JMP dword PM_CS_386:(PM_ROM_BASE+Start32) ;Long JMP to absolute
;start of PM CODE
Start32:
…… All the above code works fine. I have written many monitor type commands that work in PM mode
The problem starts here:-
For initial simplicity I have laid down the PM Interrupt descriptor table starting at 0H in RAM. Filling every entry to point to a simple message:-
PM_INT_TEST:
mov ebx,INT_SIGNON ;Send a signon message
call P_PRINT_STRING
CLD ;Default to direction is up
mov ebx,0 ;IDT starts at 0H in RAM
mov cx,100H ;Fill all (256 or 100H) 80386
;interrupts
p_fill_ints:
mov word [ebx],PM_error_int ;Offset (below) of error routine
mov word [ebx+2],PM_CS_386 ;Protect mode 386 code
mov byte [ebx+4],0
mov byte [ebx+5],10001110B ;P=1 (enable), DPL=0, 01110= 80386
;Interrupt Gate (8EH))
mov word [ebx+6],0 ;Offset 31...16
add ebx,8 ;Point to next int entry
loop p_fill_ints ;Fill in all 256 ebntries
MOV CL,’*’
call P_CO ;Consol output to show we got here.
INT 4 ;<<< To test overflow int
JMP Test_Done ;We are done
PM_error_int: ;Unassigned Interrupt warning
pushad
MOV CL,'@' ;Put an ‘@’ character on screen
call P_CO
popad
iret
<<< PROBLEM >>> ANY INERRUPT NUMBER I USE NEVER ARRIVES AT PM_error_int:
(Seems to go to an non-defined location).
;======================== 80386 DESCRIPTOR TABLES =================
ALIGN 4 ;80386 DESCRIPTOR TABLES
Gdt: DD 0 ;GDT[0]: Null entry, never used.
DD 0
;GDT[1]:Executable, read-only code,
;base address of 0,
;limit of FFFFFh, granularity bit (G)
; set (making the ;limit 4GB)
DW 0FFFFh ;Limit[15..0]
DW 0000h ;Base[15..0]
DB 00h ;Base[23..16]
DB 10011010b ;P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
DB 11001111b ;G(1) D(1) 0 0 Limit[19..16]
DB 00h ;Base[31..24]
;GDT[2]: Writable data segment,
;covering the same
;address space than GDT[1].
DW 0FFFFh ;Limit[15..0]
DW 0000h ;Base[15..0]
DB 00h ;Base[23..16]
DB 10010010b ;P(1) DPL(00) S(1) 0 E(0) W(1) A(0)
DB 11001111b ;G(1) B(1) 0 0 Limit[19..16]
DB 00h ;Base[31..24]
;GDT[3]:Executable, read-only 16 bit
;code, base address
;of 0, limit of FFFFh,
;granularity bit (G) set (making the
;limit 64K)
DW 0FFFFh ;Limit[15..0]
DW 0000h ;Base[15..0]
DB 0Eh ;Base[23..16] (>>>> Note base is @ E000:0H <<<<)
DB 10011010b ;P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
DB 00000000b ;G(0) D(0) 0 0 Limit[19..16]
DB 00h ;Base[31..24]
;GDT[4]: Writable data segment, 16 bit
;code, base ;address of 0, limit of
;FFFFh.
DW 0FFFFh ;Limit[15..0]
DW 0000h ;Base[15..0]
DB 0Eh ;Base[23..16] (>>>> Note base is @ E000:0H <<<<)
DB 10010010b ;P(1) DPL(00) S(1) 0 E(0) W(1) A(0)
DB 00000000b ;G(0) B(0) 0 0 Limit[19..16]
DB 00h ;Base[31..24]
GDT_SIZE EQU ($ - Gdt) ;<<<< Size of GDT, in bytes
GdtDesc: ;GDT descriptor
DW GDT_SIZE - 1 ;GDT limit
DD 000E0000H ;GDT base address (Note, relocated to
;0E0000H in RAM above)
IdtDesc: ;IDT descriptor
DW IDT_SIZE - 1 ;IDT limit
DD 0H ;IDT base address = 0 for testing
ridt: DW 03ffh ; The base of the IDT for real-mode
;interrupts use
DD 0 ;use when we go back to real mode
;(works fine)