News:

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

When to use CLI and when not.

Started by Dinosaur, July 01, 2005, 12:21:40 AM

Previous topic - Next topic

dioxin

Michael,
     the tolerance of the cheapest TV crystal is typically 50ppm untrimmed (which is cheaper so that's what the PC will do!) so the frequency won't be exact but could vary by +/- 60Hz from the nominal 1.193181MHz. It's about as accurate as a cheap wristwatch (in the order of 5 seconds a day).



  <<"The IR input can remain high without generating another interrupt." >>

  The IR input (to the PIC) can remain high but the INTR pin on the CPU must be cleared if you want to re-enable interrupts while still in the interrupt service routine. In the case of a standard PC with the 8259A then you would need to ensure that the End of Interrupt command is issued to the PIC before re-enabling interrupts so the PIC will de-activate the INTR line to the CPU.
   It's still a bad idea to do this, there really shouldn't be any need to mess with the CPU interrupt enable flag DURING an interrupt service routine. It's just asking for trouble.



  I realise now that I might have caused more confusion than I should. My original comments were meant to address the title of the thread "When to use CLI and when not." and I did so from the point of view of the CPU which has the 1 INTR line to handle the interrupts. You should only use CLI/STI outside of the Interrupt service routine and only where you absolutely must stop interrupts occuring such as during updating of interrupt vectors or interfacing with certain hardware.

   Of course, from the users point of view, the INTR line of the CPU isn't the one with access, it's the IRx lines of the PIC which can be programmed to behave differently.

Paul.
 

Dinosaur

Hi all

Once again I have learned from the group.

I misread your message Paul, and of-course I was talking about the Interupt controller and you were not.
I now have a much better idea on where and when to use the CLI / STI,
and after looking at my code, there is no need at all. All the bit/bashing has been replaced.
Except of course during the re-assignment of the handler.

I was going to check Paul's timing, but realised that the only reference I have is another crystal
on the adc board, which would suffer from the same inaccuracies as mentioned.

Bit like using your bathroom scales to complain about an underweight packet of chips.

Anyhow, the mSec timer is only used to time crtitical functions that happen hundreds of times per minute,
so an error of 5 sec in 24hrs will make little difference on function that may take 80 or 81 mSec.

Thanks for all the help. :U

Regards
Dinosaur

Dinosaur

#17
Hi all

Sounds like I jumped the gun.
Still having trouble.

The routine below is called by Basic to clear memory in "Unreal" mode.
Works great, as long as I leave the CLI /STI in place, otherwise it hangs.
Suspect that setting DS to zero is the reason.
An Interupt not pushing/popping DS ??, so I checked out all interupt routines.

Changed the pushing of individual registers used, to Pusha and Popa to no avail.
Any suggestions.?


;--------------------------------------
;WipeMem puts 0 in Unreal Flat Memory
;   29-12-2004
;--------------------------------------
   .MODEL MEDIUM
   .486
   .CODE
   PUBLIC   ClrMem
   ;------------------
ClrMem    PROC    FAR
   PUSH    BP
   MOV     BP,SP
   PUSHA                                ;EDIT This causes hang as well, DS must be pushed seperately.
   ;------------------
   cli
   xor      eax,eax                  
   mov      ds,ax                       ;DS = AX = 0  ..Use LINEAR ADDRESSING !
   mov      eax,400000h               ;Start is beginning of 4th MegaByte
   mov      ecx,200000h               ;do it up to start of 6th Megabyte
Clear:
   mov      word ptr DS:[EAX],00h       ;Clear memory !!!
   add      eax,2
   loopd   Clear
   sti
   ;------------------
   POPA
   POP     BP
   RET     2
ClrMem      ENDP
;----------------------
   END


Indexed addressing was never my strength, so perhaps a suggestion on how to
improve my indexing would also be appreciated.

Regards
Dinosaur

MichaelW

Quote
The IR input (to the PIC) can remain high but the INTR pin on the CPU must be cleared if you want to re-enable interrupts while still in the interrupt service routine. In the case of a standard PC with the 8259A then you would need to ensure that the End of Interrupt command is issued to the PIC before re-enabling interrupts so the PIC will de-activate the INTR line to the CPU.

On a standard PC the interrupt controller will set the INT line inactive after the second interrupt acknowledge cycle, before the interrupt is actually processed. You can verify this on page 17-18 of the 8259A data sheet available here:

http://www.electro-tech-online.com/datasheets/8259a_intel.pdf

Quote
It's still a bad idea to do this, there really shouldn't be any need to mess with the CPU interrupt enable flag DURING an interrupt service routine. It's just asking for trouble.

I regard it as normal practice, when necessary, and I know I'm not alone in this. The IRQ0 and IRQ1 handlers for the recent AWARD BIOS that I checked just now both execute an STI as the second instruction. While I do agree that most of the time there is no need to enable interrupts, situations can arise where you need to do significant processing in the handler, before chaining to the previous handler, and where you cannot leave the higher-priority interrupts disabled.
eschew obfuscation

MichaelW

Dinosaur,

PUSHA and POPA do not affect the segment registers.

Procedures called by the Microsoft DOS basics must generally preserve the direction flag and the BP, DI, SI, DS, and SS registers. You could use ES without preserving it.

The procedure does not appear to take an argument, but your return instruction is adjusting the stack pointer on return.

I don't know what else is running in your system, but if any PM code writes to a segment register the segment limit could be altered. Beyond that, I have no idea how leaving interrupts enabled could cause a problem.
eschew obfuscation

Dinosaur

Michael

The routine was actually written as a Function, so that it can return a value.
Functions are also easier to implement in complex Basic code.
The penalty is that you have to clean up the stack even if you dont send a value back.

I will try saving all the flags and segment registers and see if that allows me to remove the CLI/STI

Regards
Dinosaur

MichaelW

A function has the same calling conventions as a sub procedure. If no arguments are pushed onto the stack before the call, then none need be removed on return. The unnecessary SP adjustment would have no effect if the function were called only from module-level code, but if it were called from a procedure the program would crash when that procedure returned to the wrong address.
eschew obfuscation

dioxin

Michael,
   <<On a standard PC the interrupt controller will set the INT line inactive after the second interrupt acknowledge cycle, before the interrupt is actually processed.>>

   Not so. At least not necessarily so.
   This only happens if the PIC is set to do an automatic end of interrupt. I don't know about yours but I have a PC which is definitely NOT set to do that and requires a manual EOI command to be sent to the PIC in order to clear it.


   <<just now both execute an STI as the second instruction>>
      
   What's the first instruction? If it isn't a call to the main processing routine then there are serious problems.

   Imagine this scenario.
   INT0 and INT7 occur simultaneously.
   You expect the high priority INT0 to be processed and then the low priority INT7.

   If your 2 statements are correct then the following will happen instead..
   INT0 and INT7 lines go high.
   The 2 corresponding bits in the PIC IRR get set.
   The PIC correctly prioritises these and knows that the vector to pass to the CPU is that of INT0 as it is the higher priority.
   The INT0 vector goes to the CPU which acknowledges this and immediately causes the automatic EOI to clear the PIC ISR bit for INT0.
   The interrupt is just about to get CPU time when..
   The PIC now sees that INT0 is no longer in service as it was automatically cleared immediately but there is a pending INT7.
   The PIC sees INT7 as the next priority interrupt and issues an interrupt request to the CPU.

   At this point, the CPU would normally ignore the request because the interrupt enable flag of the CPU is clear.. but you say your interrupt routine immediately re-enables the interrupts by executing STI. So, in your case, the high priority INT0 routine gets to the second instruction (STI) and the CPU is immediately interrupted by the low priority INT7 which is pending.

   INT7 now completes (assuming no further interrupts occur) and, on return, INT0 completes.

   If what you say is true then the the lower priority interrupts end up being given priority over the high priority ones!


   I'd guess that the Automatic EOI is not being used and that the interrupt service routine is clearing the interrupt manually before it exits.

   It still has the problem that, with the STI at the start of the interrupt service routine, that the routine must be carefully written to be re-entrant and able to cope with being called multiple times before it completes the first one, i.e.it needs to cope with nested interrupt calls, otherwise interrupt calls will be missed. I know it's not impossible, but it's an unnecessary complication.
   It should be very rare for an interrupt routine to take long enough to be a problem. If it is then certain critical stuff needs to be done before the STI so that the routine can correctly handle, in effect, multiple interrupts of the same priority being processed at different points in the same interrupt service routine.


   <<The IRQ0 and IRQ1 handlers for the recent AWARD BIOS that I checked>>

   Strange, I could see it being useful for lower priority interrupts to allow the higher riority ones to jump in in certain circumstances, but INT0 should never need it, what else other than another INT0 can interrupt it?

Paul.

Dinosaur

Hi all


DECLARE FUNCTION ClrMem& (A&)

IF ClrMem&(0)  <> 0 THEN             ;one way to use it if a value is passed

A& = ClrMem&(0)                          ;the other way. A& is ignored if no value passed.


When running through codeview, it showed an imbalance on return of 2, so RET 2

Quote
but if it were called from a procedure the program would crash when that procedure returned to the wrong address.

How does CLI/STI make difference in this case.?

Regards

MichaelW

Paul,

Just to make sure we are both on the same page here, by standard PC I mean one that follows the industry standard design that was originally based on the IBM PC-AT. While not all PCs have followed this design (EISA and MCA systems did not), the vast majority have. Within this design, the hardware interrupt subsystem is programmed (by the BIOS) just as it was for the PC-AT, and the hardware interrupts work just as they did for the PC-AT. This means no automatic EOI, and edge-sensitive triggering for the interrupt requests.

The scenario that you describe would be correct for automatic EOI mode. In AP-59, Using the 8259A Programmable Interrupt Controller, Intel describes the problem with automatic EOI disturbing the fully nested mode, as well as the problem with "over nesting", where an IR input keeps interrupting its own routine.

In the data sheet that I linked, Figure 10 on page 18 clearly shows that INT goes inactive at the end of the second interrupt acknowledge cycle. AP-59 includes this same figure, as well as:
Quote
When the IR input is in an inactive state (LOW), the edge sense latch is set. If edge sensitive triggering is selected, the "Q" output of the edge sense latch will arm the input gate to the request latch. This input gate will be disarmed after the IR input goes active (HIGH) and the interrupt request has been acknowledged. This disables the input from generating any further interrupts until it has returned low to re-arm the edge sense latch. If level sensitive triggering is selected, the "Q" output of the edge sense latch is rendered useless. This means the level of the IR input is in complete control of interrupt generation; the input won't be disarmed once acknowledged.
...
Immediately after the interrupt acknowledge sequence, the PR sets the corresponding bit in the ISR which simultaneously clears the edge sense latch. If edge-sensitive triggering is used, clearing the edge sense latch also disarms the request latch. This inhibits the possibility of a still active IR input from propagating through the priority cell. The IR input must return to an inactive state, setting the edge sense latch, before another interrupt request can be recognized. If level sensitive triggering is used, however, clearing the edge sense latch has no effect on the request latch. The state of the request latch is entirely dependent on the IR input level. Another interrupt will be generated immediately if the IR level is left active after its ISR bit has been reset. An ISR bit gets reset with an End-of-Interrupt (EOI) command issued in the service routine.

And the program in the attachment that I posted, the code for which is repeated below, run on the two systems that I currently have available, clearly indicates that each interrupt event is generating only one call to the handler.


.model small, c
.386
.stack
.data
.code
.startup

    ; Put data handlers need to access in code segment.
    jmp   @F
    prevIRQ0Handler dd 0
    prevIRQ1Handler dd 0
    counter         dw 0
  @@:
 
    ; Hook interrupt 8.
    mov   ax,3508h
    int   21h
    mov   word ptr prevIRQ0Handler,bx
    mov   word ptr prevIRQ0Handler[2],es
    push  ds
    mov   ax,2508h
    push  cs
    pop   ds
    mov   dx,offset IRQ0Handler
    int   21h
    pop   ds
 
    ; Hook interrupt 9.
    mov   ax,3509h
    int   21h
    mov   word ptr prevIRQ1Handler,bx
    mov   word ptr prevIRQ1Handler[2],es
    push  ds
    mov   ax,2509h
    push  cs
    pop   ds
    mov   dx,offset IRQ1Handler
    int   21h
    pop   ds
 
    ; Wait for user to press Escape.
  @@:
    mov   ah,0
    int   16h
    cmp   al,27
    jne   @B

    ; Unhook and exit.   
    push  ds
    lds   dx,prevIRQ0Handler
    mov   ax,2508h
    int   21h
    pop   ds
    push  ds
    lds   dx,prevIRQ1Handler
    mov   ax,2509h
    int   21h
    pop   ds   

.exit
 
; =========================================================
; This will display once per 18 ticks.
; =========================================================
IRQ0Handler:

    ; Enable interrupts immediately.
    sti
   
    inc   cs:counter

    .IF (cs:counter == 18)

      mov   cs:counter,0
     
      push  ax
      push  bx

      ; Display a '0'.
      mov   bx,0
      mov   ah,0eh
      mov   al,'0'
      int   10h

      pop   bx
      pop   ax

    .ENDIF

    ; Chain to the previous handler.
    jmp   cs:prevIRQ0Handler

; =========================================================
; This will be called at least once each time a key is
; pressed or released, and more than once for most of
; the extended keys.
; =========================================================
IRQ1Handler:

    ; Enable interrupts immediately.
    sti
   
    push  ax
    push  bx

    ; Display a '1'.
    mov   bx,0
    mov   ah,0eh
    mov   al,'1'
    int   10h

    pop   bx
    pop   ax

    ; Chain to the previous handler.
    jmp   cs:prevIRQ1Handler

end


The AWARD BIOS that I checked is v4.51PG, 09/19/2000, on a GigaByte GA-5AX. The first instruction at the entry points is a near jump to the actual handler, and the first instruction in the handler is an STI.
eschew obfuscation

MichaelW

Dinosaur,

I don't understand. If you are passing a parameter to the function, it is being ignored, and the function as coded will always return zero.

eschew obfuscation

Dinosaur

Michael

You are right, it always returns Zero. However it still performs it's task of clearing the memory.
Some people call it laziness, I call it forward planning.

If I declare all functions to accept a command and return a value, then whenever I need to expand the function
a simple change of code is all that is needed in the asm function. The Basic code does not need to be changed other
then the value it checks for. You could argue that it will be slower having to push and pop values (that dont exist) .

To satisfy my curiosity, I ran it as a routine to confirm the CLI/STI problem, but it still exists.
I will try to push flags and other registers. Have just been a bit busy.

Regards
Dinosaur