15

The 1979 version of the 8086 family user's manual is available at different places in the internet, see 1,2,3. It seems there is no newer version available. This manual documents the single-stepping feature in figure 2-29 on page 2-23 and figure 2-31 on page 2-27. Are these diagrams accurate? The emphasis of this question is in the corner cases like concurrent request of single stepping, external interrupts and internal interrupts.

19

It seems the diagrams are not accurate. I wrote a test program that traces INT1 invocations (and delays execution during INT1 to increase the chance of getting hit by timer interrupts) while executing the following machine language fragment:

pushf
mov  ax, 300h      ; 100h = TF; 200h = IF
push ax
popf               ; This instruction sets the trace flag
mov  ax, 1234h
mov  bl, 1    
inc  ax       
mov  dx, ds   
mov  es, ax        ; On 8086/8088: No interrupts before NOP is executed
nop           
mov  dx, ss   
mov  ss, dx        ; On any x86 processor: No interrupts before NOP is executed
nop           
int  0A1h     
dec  cx       
popf               ; This instruction clears the trace flag
dec  ax       

The results of executing this fragments on an 8088-1 clocked at 10 MHz are:

  • No INT1 is generated after executing popf. This is surprising, because the flow diagram 2-29 contains the step COMPLETE CURRENT INSTRUCTION before the branch on TF. After completing popf, the TF is set, and the action of entering the single-step handler should be taken.
  • Also no INT1 is generated after the subsequent mov ax, 1234h instruction. So even if we assume TF in diagram 2-29 to mean the state of TF at the beginning of execution an instruction, we observe a further delay of one instruction. This is comparable to the effect that STI also enable interrupts to be recognized only after the subsequent instruction (the manual explicitly says so on page 2-48).
  • If an external interrupt (e.g. a timer interrupt) is recognized, the 8088 triggers a single-step interrupt after pushing the state to the stack and clearing IF and TF, just as the datasheet shows in diagram 2-29 and explains on page 2-28: "If the processor is single-stepping, it processes an interrupt (either internal or external) as follows. Control is passed normally (flags, CS and IP are pushed) to the procedure designated to handle the type of interrupt that has occurred. However, before the first instruction of that procedure is executed, the single-step interrupt is "recognized" and control is passed normally (flags, CS and IP are pushed) to the type 1 interrupt procedure".
  • During execution of the interrupt handlers, no single-stepping occurrs, because the processor clears TF on interrupt entry.
  • When an external interrupt returns with IRET, another INT1 is triggered before the next instruction of the interrupted program is executed. This seems to contradict Figure 2-31. The flow diagram shows the recognition of the single step condition before the first IRQ handler instruction is executed, but on return of the IRQ handler it goes straight into EXECUTE NEXT INSTRUCTION without recognizing the single step interrupt.
  • On the other hand, if an internal interrupt is caused by executing an instruction (like the int 0A1h instruction; the vector points directly to IRET), there is in fact no INT1 after the INT A1 returns before dec cx gets executed. The datasheet is accurate on this topic.
  • After clearing the TF by issuing POPF at the end of the fragment, a last INT1 is triggered, with the return address pointing to DEC AX. Again, this is an effect of delayed recognition of changes to TF I could not find in the data sheet.
  • If an external interrupt is requested during execution of the single-step handler, the external interrupt is entered after returning from the single-step handler before executing the next instruction.
  • Even though the instruction following int 0A1h (i.e. dec cx) is not seen as return address of a single-step handler invocation, an external interrupt can be recognized at that point, and one might see a single-step invocation on the first instruction of the timer interrupt, with the return address of the timer interrupt pointing to the instruction dec cx.

To expand on the second-to-last item: I observed the following pattern:

  1. mov bl, 1 is executed
  2. the single-step handler is entered, with the return address pointing to the subsequent instruction inc ax
  3. the single-step handler is entered again, with the return address pointing to the first instruction of the timer interrupt handler. The return address of the timer interrupt handler is still the instruction inc ax.
  4. (the timer interrupt handler is executed without further single-step invocations)
  5. inc ax is executed
  6. the single-step handler is entered, with the return address pointing to the instruction mov dx, ds

This behaviour means that you can not rely on the return instruction of the single-step handler being executed before the single-step handler might be entered again. This detail tripped me in my 286 emulator attempt.

This behaviour also means that if the combined process of the single-step handler and the timer interrupt handler is slow enough that it takes longer than a time tick, the processor might be spinning in timer interrupt executions (with a single-step invocation on the first instruction each) without any instructions of the interrupted code getting executed inbetween. I verified this by adding a 50 millisecond delay into the single-step code with standard PC/DOS timer period of 55ms, and observed a burst of 6 single-step handler invocations, all of them pointing to the entry point of the timer interrupt, without any change of the timer interrupt return adderss.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.