/* Copyright (C) 1994, 1995 Charles Sandmann (sandmann@clio.rice.edu) * This file maybe freely distributed and modified as long as copyright remains. */ #if (GAS_MAJOR == 2) \ && ((GAS_MINOR < 9) || ((GAS_MINOR == 9) && (GAS_MINORMINOR < 5))) #define LJMP(there) \ ljmp there #define LCALL(there) \ lcall there #else #define LJMP(there) \ ljmp *there #define LCALL(there) \ lcall *there #endif #define EAX 0 #define EBX 4 #define ECX 8 #define EDX 12 #define ESI 16 #define EDI 20 #define EBP 24 #define ESP 28 #define EIP 32 #define EFLAGS 36 #define CS 40 #define DS 42 #define ES 44 #define FS 46 #define GS 48 #define SS 50 #define ERRCODE 52 #define EXCEPNO 56 #define PREVEXC 60 /* Length 64 bytes plus non-used FPU */ .data .balign 8 .comm exception_stack, 8000 .text .balign 16,,7 #define EXCEPTION_ENTRY(number) \ pushl number ; \ jmp exception_handler .global ___djgpp_exception_table ___djgpp_exception_table: EXCEPTION_ENTRY($0) EXCEPTION_ENTRY($1) EXCEPTION_ENTRY($2) EXCEPTION_ENTRY($3) EXCEPTION_ENTRY($4) EXCEPTION_ENTRY($5) EXCEPTION_ENTRY($6) EXCEPTION_ENTRY($7) EXCEPTION_ENTRY($8) EXCEPTION_ENTRY($9) EXCEPTION_ENTRY($10) EXCEPTION_ENTRY($11) EXCEPTION_ENTRY($12) EXCEPTION_ENTRY($13) EXCEPTION_ENTRY($14) EXCEPTION_ENTRY($15) EXCEPTION_ENTRY($16) EXCEPTION_ENTRY($17) /* This code is called any time an exception occurs in the 32 bit protected ;* mode code. The exception number is pushed on the stack. This is called ;* on a locked stack with interrupts disabled. Don't try to terminate. ;* ;* [ * | SS ] * Don't modify ;* [ ESP ] ;* [ EFLAGS ] ;* [ * | CS ] * Don't modify ;* [ EIP ] ;* [ ERR CODE ] ;* [ * |RET CS*] * Don't modify ;* [ RET EIP* ] * Don't modify ;* [ EXCEPTION # ] (And later EBP) ;*/ /* ;WARNING WARNING WARNING ;The mechanism for passing signals between the debugger ;and the debuggee relies on the *exact* instructions between ;EXCEPTION_ENTRY($13) above and "cmpb $0, forced" instruction ;below! These instructions are stored in forced_test[] buffer ;in src/debug/common/dbgcom.c. Do NOT change anything between ;these two instructions, or you will break signal support in ;the debuggers!! */ exception_handler: pushl %ebx pushl %ds .byte 0x2e /* CS: */ cmpb $0, forced je not_forced call limitFix .byte 0x2e /* CS: */ movzbl forced,%ebx movl %ebx,8(%esp) /* replace EXCEPNO */ not_forced: movw %cs:___djgpp_our_DS, %ds movl $0x10000, forced /* its zero now, flag inuse */ movl $exception_state, %ebx popl DS(%ebx) popl EBX(%ebx) popl EXCEPNO(%ebx) movl %esi, ESI(%ebx) movl %edi, EDI(%ebx) movl %ebp, EBP(%ebx) movl %eax, EAX(%ebx) movl %ecx, ECX(%ebx) movl %edx, EDX(%ebx) movw %es, ES(%ebx) movw %fs, FS(%ebx) movw %gs, GS(%ebx) movl ___djgpp_exception_state_ptr, %eax movl %eax, PREVEXC(%ebx) /* Stack clean at this point, DS:[EBX] points to exception_state, all register information saved. Now get the info on stack. */ pushl %ebp movl %esp, %ebp /* load ebp with stack for easy access */ movl 12(%ebp), %eax movl %eax, ERRCODE(%ebx) movl 16(%ebp), %eax movl %eax, EIP(%ebx) movl 20(%ebp), %eax movw %ax, CS(%ebx) movl 24(%ebp), %eax movl %eax, EFLAGS(%ebx) andb $0xfe, %ah /* Clear trace flag */ movl %eax, 24(%ebp) /* and restore on stack */ movl 28(%ebp), %eax movl %eax, ESP(%ebx) movl 32(%ebp), %eax movw %ax, SS(%ebx) movl $dpmi_exception_proc1, 16(%ebp) /* where to return */ movw %cs, 20(%ebp) /* Change to our local stack on return from exception (maybe stack exception) */ movw %ds, %ax cmpb $12,EXCEPNO(%ebx) /* Stack fault ? */ je 1f cmpw %ax,32(%ebp) je stack_ok .byte 0x2e /* CS: */ movw ___excep_ds_alias,%di cmpw %di,32(%ebp) /* if it's DS alias, switch to normal DS */ jne 1f movw %ax,32(%ebp) jmp stack_ok 1: movl $exception_stack+8000, 28(%ebp) movw %ax, 32(%ebp) stack_ok: /* Now copy the exception structure to the new stack before returning */ movw %ax, %es movl %ebx,%esi movl 28(%ebp), %edi subl $92, %edi /* 64 plus extra for longjmp */ movl %edi, 28(%ebp) movl %edi, ___djgpp_exception_state_ptr movl $16, %ecx cld rep movsl movl EAX(%ebx), %eax /* restore regs */ movl ESI(%ebx), %esi movl EDI(%ebx), %edi movl ECX(%ebx), %ecx movw ES(%ebx), %es popl %ebp pushl EBX(%ebx) pushl DS(%ebx) movb $0, forced+2 /* flag non-use */ popl %ds popl %ebx lret /* Code to fix fake exception, EBX destroyed. Note, app_DS may == our_DS! */ .balign 16,,7 limitFix: pushl %eax pushl %ecx pushl %edx .byte 0x2e /* CS: */ movl ___djgpp_app_DS, %ebx /* avoid size prefix */ .byte 0x2e /* CS: */ movl ds_limit, %edx movl %edx, %ecx shrl $16, %ecx movw $0x0008, %ax int $0x31 /* Set segment limit */ popl %edx popl %ecx popl %eax ret /* This local routine preprocesses a return request to the C code. It checks to make sure the DS & SS are set OK for C code. If not, it sets them up */ .balign 16,,7 dpmi_exception_proc1: cld .byte 0x2e /* CS: !!! */ movw ___djgpp_our_DS, %bx /* to be sure */ movw %bx, %ds movw %bx, %es /* Note: SS:ESP should be set properly by exception routine */ jmp ___djgpp_exception_processor /* This code is called by a user routine wishing to save an interrupt ;* state. It will return with a clean stack, our DS,ES,SS. ;* Minor bug: uses static exception_state for a short window without ;* interrupts guaranteed disabled. ;* ;* [ EFLAGS ] ;* [ * | CS ] ;* [ EIP ] ;* [ CALLING EIP ] ;*/ .balign 16,,7 .globl ___djgpp_save_interrupt_regs ___djgpp_save_interrupt_regs: pushl %esi pushl %ds movw %cs:___djgpp_our_DS, %ds movl $exception_state, %esi popl DS(%esi) /* Trashes ES but OK */ popl ESI(%esi) movl %edi, EDI(%esi) movl %ebp, EBP(%esi) movl %eax, EAX(%esi) movl %ebx, EBX(%esi) movl %ecx, ECX(%esi) movl %edx, EDX(%esi) popl %edx /* Save calling EIP */ popl EIP(%esi) popl %eax movw %ax,CS(%esi) /* Don't pop, nukes DS */ popl EFLAGS(%esi) movl %esp, ESP(%esi) movw %es, ES(%esi) movw %fs, FS(%esi) movw %gs, GS(%esi) movw %ss, SS(%esi) movl ___djgpp_exception_state_ptr, %eax movl %eax, PREVEXC(%esi) cld movw %ds, %ax movw %ax, %es movw %ss, %bx cmpw %ax, %bx /* is SS = DS ? */ je Lss_ok movw %ax, %ss /* set new SS:ESP */ movl $exception_stack+8000, %esp Lss_ok: subl $92, %esp /* 64 plus extra for longjmp */ movl %esp, %edi movl $16, %ecx movl %edi, ___djgpp_exception_state_ptr rep movsl /* Copy structure to stack */ jmp *%edx /* A "return" */ .balign 8 /* We will touch this; it must be locked */ .global ___djgpp_hw_lock_start ___djgpp_hw_lock_start: /* src/debug/common/dbgcom.c knows that `ds_limit' is stored 4 bytes before `forced' and relies on that. Do NOT change that! */ ds_limit: .long 0 forced: .long 0 .global ___djgpp_cbrk_count ___djgpp_cbrk_count: .long 0 .global ___djgpp_timer_countdown ___djgpp_timer_countdown: .long 0 .global ___djgpp_our_DS ___djgpp_our_DS: .word 0 .global ___djgpp_app_DS ___djgpp_app_DS: .word 0 .global ___djgpp_dos_sel ___djgpp_dos_sel: .word 0 .global ___djgpp_hwint_flags ___djgpp_hwint_flags: .word 0 .global ___djgpp_sigint_key ___djgpp_sigint_key: .word 0 /* scan code and kb status */ .global ___djgpp_sigint_mask ___djgpp_sigint_mask: .word 0 /* kb status mask */ .global ___djgpp_sigquit_key ___djgpp_sigquit_key: .word 0 .global ___djgpp_sigquit_mask ___djgpp_sigquit_mask: .word 0 .global ___djgpp_old_kbd ___djgpp_old_kbd: .long 0,0 .global ___djgpp_old_timer ___djgpp_old_timer: .long 0,0 .global ___djgpp_exception_state_ptr ___djgpp_exception_state_ptr: .long 0 exception_state: .space 64 .global ___excep_ds_alias ___excep_ds_alias: .word 0 /* private locked __djgpp_ds_alias */ .balign 16,,7 .global ___djgpp_npx_hdlr ___djgpp_npx_hdlr: pushl %eax xorl %eax,%eax outb %al,$0x0f0 movb $0x20,%al outb %al,$0x0a0 outb %al,$0x020 movb $0x75,%al hw_to_excp: call ___djgpp_hw_exception popl %eax sti iret .balign 16,,7 .global ___djgpp_kbd_hdlr ___djgpp_kbd_hdlr: pushl %eax pushl %ebx pushl %ds .byte 0x2e /* CS: */ testb $1, ___djgpp_hwint_flags /* Disable? */ jne Lkbd_chain movw %cs:___djgpp_dos_sel, %ds /* Conventional mem selector */ /* movw $0x7021,0xb0f00 */ /* Test code - write to mono */ /* Check Keyboard status bits */ movb 0x417,%ah /* Get KB status byte */ testb $1,%ah je 6f orb $2,%ah /* If RShift is set, set LShift as well */ 6: inb $0x60,%al /* Read the scan code */ 99: movb %ah,%bh /* Save KB status */ andb %cs:___djgpp_sigint_mask, %ah /* Mask off irrelevant bits */ cmpw %cs:___djgpp_sigint_key, %ax /* Test for SIGINT */ jne 7f movb $0x79,%bh /* SIGINT */ jmp 98f 7: movb %bh,%ah /* Restore KB status */ andb %cs:___djgpp_sigquit_mask, %ah /* Mask off irrelevant bits */ cmpw %cs:___djgpp_sigquit_key, %ax /* Test for SIGQUIT*/ jne Lkbd_chain movb $0x7a,%bh /* SIGQUIT */ /* Clear interrupt, (later: remove byte from controller?) movb $0x20,%al outb %al,$0x020 */ 98: movb %bh,%al call ___djgpp_hw_exception Lkbd_chain: popl %ds popl %ebx popl %eax LJMP(%cs:___djgpp_old_kbd) .balign 16,,7 .global ___djgpp_kbd_hdlr_pc98 ___djgpp_kbd_hdlr_pc98: pushl %eax pushl %ebx pushl %ds .byte 0x2e /* CS: */ testb $1, ___djgpp_hwint_flags /* Disable? */ jne Lkbd_chain /* Check CTRL state */ movw %cs:___djgpp_dos_sel, %ds /* Conventional mem selector */ movb 0x053a,%al /* Get KB status byte */ /* Convert PC98 style status byte to PC/AT style */ movb %al,%ah andb $0x09,%ah /* GRPH(=ALT), SHIFT(=R-Shift) */ testb $0x02,%al je 981f orb $0x40,%ah /* CAPS(=Caps Lock) */ 981: testb $0x10,%al je 982f orb $0x04,%ah /* CTRL(=Ctrl) */ 982: testb $0x01,%al je 983f orb $0x02,%ah /* SHIFT(=L-Shift) */ 983: testb $0x04,%al je 984f orb $0x20,%ah /* KANA(=NUM Lock) */ 984: inb $0x41,%al /* Read the scan code */ jmp 99b .balign 16,,7 .global ___djgpp_timer_hdlr ___djgpp_timer_hdlr: .byte 0x2e /* CS: */ cmpl $0,___djgpp_timer_countdown je 4f pushl %ds movw %cs:___excep_ds_alias, %ds decl ___djgpp_timer_countdown popl %ds jmp 3f 4: pushl %eax movb $0x78,%al call ___djgpp_hw_exception popl %eax 3: .byte 0x2e /* CS: */ testb $4, ___djgpp_hwint_flags /* IRET or chain? */ jne 2f LJMP(%cs:___djgpp_old_timer) 2: pushl %eax movb $0x20,%al /* EOI the interrupt */ outb %al,$0x020 popl %eax iret /* On entry ES is the DS alias selector */ .balign 16,,7 .global ___djgpp_cbrk_hdlr /* A RMCB handler for 0x1b */ ___djgpp_cbrk_hdlr: cld lodsl /* EAX = DS:[esi] CS:IP */ movl %eax, %es:0x2a(%edi) /* store in structure */ lodsl /* AX = FLAGS */ movw %ax, %es:0x20(%edi) addw $6, %es:0x2e(%edi) /* Adjust RM SP */ movb $0x1b,%al .byte 0x2e /* CS: */ testb $2, ___djgpp_hwint_flags /* Count, don't kill */ jne 1f call ___djgpp_hw_exception iret 1: incl %es:___djgpp_cbrk_count iret .global ___djgpp_i24 /* Int 24 handler if needed */ .global ___djgpp_iret /* Int 23 handler if needed */ ___djgpp_i24: movb $3,%al ___djgpp_iret: iret /* Code to stop execution ASAP, EAX destroyed. Make DS/ES/SS invalid. Fake exception value is passed in AL and moved into the "forced" variable. This is used to convert a HW interrupt into something we can transfer control away from via longjmp or exit(), common with SIGINT, SIGFPE, or if we want EIP information on timers. */ .balign 16,,7 .global ___djgpp_hw_exception ___djgpp_hw_exception: .byte 0x2e /* CS: */ cmpl $0, forced /* Already flagged? */ jne already_forced pushl %ebx pushl %ecx pushl %edx pushl %ds movw %cs:___djgpp_our_DS, %ds movl ___djgpp_app_DS, %ebx /* avoid size prefix */ lsl %ebx, %ecx movl %ecx, ds_limit /* Save current limit */ movb %al, forced /* Indicate a fake exception */ xorl %ecx, %ecx movw $0xfff, %dx /* 4K limit is null page ! */ movw $0x0008, %ax int $0x31 /* Set segment limit */ 5: popl %ds popl %edx popl %ecx popl %ebx already_forced: ret .global ___djgpp_hw_lock_end ___djgpp_hw_lock_end: ret /* LD does weird things */