Image Map Image Map
Page 2 of 2 FirstFirst 12
Results 11 to 19 of 19

Thread: 16-bit Watcom C and memory models

  1. #11

    Default

    Quote Originally Posted by daver2 View Post
    My first thought through was exactly what reenigne had identified - what is the size of available stack when your ISR(s) are entered. if your code use too much stack (and the definition of 'too much' here is a bit variable as it is up to DOS - or whatever program is executing - when the interrupt occurs) then problems will ensue. This may also account for why things go awry when compiled with -ml - as the return addresses and anything else on the stack will be FAR pointers (i.e. use more stack space).
    I don't think it's lack of stack space that's the problem here - the three functions gf1_irq_handler, gf1_handler and HandleTimer1 probably use less than 60 bytes of stack space between them. Instead, I suspect that the interrupt routine (including HandleTimer1) is executing with the wrong value in SS, which breaks an assumption in the compiled code (that SS is 0x7fa paragraphs higher than CS).

    Quote Originally Posted by daver2 View Post
    I would allocate my own stack when an ISR is entered and return the stack (SS/SP) back to what it was before exiting the ISR. This obviously means storing the SS/SP somewhere on entry. I would store SS/SP in two general-purpose registers on entry to the ISR, then allocate a new stack (of sufficient size for your use) and PUSH the old SS/SP onto the new stack.
    The new stack has to be in the same segment as the original stack, so you have to allocate it statically (i.e. as a global array) rather than on the heap (using malloc or equivalent). How big it needs to be depends on what you're doing in the interrupt handler and its callees, but Watcom's default 2kB stack size should be more than enough.

    Quote Originally Posted by daver2 View Post
    If memory serves me correctly - storing something into SS automatically means that the next instruction isn't interruptible (i.e. the stack segment/offset isn't in an undefined state). [EDIT: See http://c9x.me/x86/html/file_module_x86_id_176.html and search for 'inhibit'].
    Except that this behaviour is buggy on some CPUs. But since interrupts should be off until after the IRET anyway, it doesn't matter in this case.

  2. #12
    Join Date
    Jun 2012
    Location
    UK - Worcester
    Posts
    2,695

    Default

    I have not used the WATCOM C/C++ compiler before (WATCOM FORTRAN yes).

    From what I can see in the documentation I have found on the internet, the registers are saved onto the stack that is in force at the time of the interrupt and DS reloaded so that access to program data can be made. Nothing is specifically mentioned about setting up SS/SP - so (my assumption would be) that the interrupt service routine is running with the 'interruptees' stack and (therefore) the size of the WATCOM stack that has been set-up is irrelevant? If you are saying that there is a requirement on the value held in SS then could you provide me with a reference to that (just for my interest). It seems somewhat 'strange' - to say the least - that there is an implied SS=CS+0x7fa paragraphs. From my reading of the documentation, the linker should store the BSS/STACK tagged data at the end of the executable program so it doesn't take up any physical size in the EXE file. It says nothing that I can find about 0x7fa paragraphs anywhere in the documentation I have read. It also 'breaks' Intel's recommendations about not performing arithmetic using segment registers (this was in preparation for protected mode if I remember correctly).

    One man's bug is another man's implementation... I remember being on an Intel 286 assembler course in the early days (with a load of IBM engineers from the UK - but that's another story) where exactly the third point of the reference you gave was raised regarding multiple overrides on a repeat string instruction. The recommendation was to always check CX after the instruction and (if non zero) branch back to the start of the instruction (including any prefixes). I suspect this was how it worked at the time - and for subsequent processors/steppings Intel decided to change the way it performed (but in an upwardly compatible manner).

    I must admit, it is 'daft' to change SS without interrupts being disabled - but I don't specifically remember anything being said about a bug (or at least not in the 8086/80286).

    Interested to learn for myself.

    It would be good to see the assembly code that has been generated by the compiler as the prolog to the _interrupt handler...

    Dave

  3. #13
    Join Date
    Mar 2011
    Location
    Atlanta, GA, USA
    Posts
    1,422

    Default

    Thanks for the feedback. I'm going to dig deeper today. I had assumed I would be running off the interruptee's stack as well - which is the program stack setup by Watcom itself. This isn't a TSR or BIOS routine - the interrupt is hooked at start of program execution and restored at the end.
    "Good engineers keep thick authoritative books on their shelf. Not for their own reference, but to throw at people who ask stupid questions; hoping a small fragment of knowledge will osmotically transfer with each cranial impact." - Me

  4. #14

    Default

    Quote Originally Posted by daver2 View Post
    From what I can see in the documentation I have found on the internet, the registers are saved onto the stack that is in force at the time of the interrupt and DS reloaded so that access to program data can be made. Nothing is specifically mentioned about setting up SS/SP - so (my assumption would be) that the interrupt service routine is running with the 'interruptees' stack and (therefore) the size of the WATCOM stack that has been set-up is irrelevant?
    Correct.

    Quote Originally Posted by daver2 View Post
    If you are saying that there is a requirement on the value held in SS then could you provide me with a reference to that (just for my interest). It seems somewhat 'strange' - to say the least - that there is an implied SS=CS+0x7fa paragraphs.
    My reference is the assembler output of the compiler for eeguru's program. Here is gf1_irq_handler:

    Code:
    gf1_irq_handler_:
        push        ax
        push        cx
        push        dx
        push        bx
        push        sp
        push        bp
        push        si
        push        di
        push        ds
        push        es
        push        ax
        push        ax
        mov         bp,sp
        cld
        mov         ax,DGROUP:CONST
        mov         ds,ax
        mov         cx,word ptr __gf1_data+0aH
        mov         bx,cx
        shl         bx,1
        shl         bx,1
        add         bx,cx
        mov         al,byte ptr __gf1_irq+2[bx]
        xor         ah,ah
        mov         dx,ax
        mov         bl,byte ptr __gf1_irq+3[bx]
        xor         bh,bh
        mov         ax,bx
        call        far ptr outp_
        cmp         cx,7
        jle         L$27
        mov         dx,20H
        mov         ax,dx
        call        far ptr outp_
    L$27:
        push        cs
        call        near ptr gf1_handler_
        pop         ax
        pop         ax
        pop         es
        pop         ds
        pop         di
        pop         si
        pop         bp
        pop         bx
        pop         bx
        pop         dx
        pop         cx
        pop         ax
        iret
    As you can see, it does not modify SS before calling gf1_handler (which is a normal function). And here is HandleTimer1:

    Code:
    HandleTimer1_:
        mov         ax,4
        call        far ptr __STK
        mov         ax,word ptr ss:_count1
        mov         ax,word ptr ss:_count1+2
        add         word ptr ss:_count1,1
        adc         word ptr ss:_count1+2,0
        retf
    The __STK function just checks for stack overflow, it doesn't modify SS or SP. As you can see, this function accesses count1 via an SS: override, i.e. it requires a particular value to be in SS such that the address encoded in the ADD and ADC instructions for count1 is correct. For this particular program (at least, when I compiled it) the required value of SS is 0x7fa paragraphs higher than CS (sorry if I mistakenly implied that value was universal and not specific to this program).

    Quote Originally Posted by daver2 View Post
    From my reading of the documentation, the linker should store the BSS/STACK tagged data at the end of the executable program so it doesn't take up any physical size in the EXE file.
    That's right.

    Quote Originally Posted by daver2 View Post
    It also 'breaks' Intel's recommendations about not performing arithmetic using segment registers (this was in preparation for protected mode if I remember correctly).
    Usually all the arithmetic is done in the startup code and/or as .EXE relocations. It's quite unavoidable to have some segment arithmetic somewhere when doing real-mode programming and accessing more than 64kB, though. I guess the recommendation is there to avoid having segment arithmetic in assembly code that might later need to be ported to protected mode.

  5. #15

    Default

    Quote Originally Posted by eeguru View Post
    Thanks for the feedback. I'm going to dig deeper today. I had assumed I would be running off the interruptee's stack as well - which is the program stack setup by Watcom itself. This isn't a TSR or BIOS routine - the interrupt is hooked at start of program execution and restored at the end.
    Right, but pieces of DOS/BIOS code (in kbhit()) and other IRQs (and therefore potentially TSRs) do get executed during the running of your program (and therefore potentially get interrupted by your handler). Any of these could set up their own stack temporarily instead of using the program's stack that Watcom set up.

  6. #16
    Join Date
    Mar 2011
    Location
    Atlanta, GA, USA
    Posts
    1,422

    Default

    Also is it reasonable to assume that if I chain to the previous handler, and assume other hardware IRQ routines will as well, that the last in chain will be a BIOS routine that performs a PIC EOI? ie. Is it save to remove my EOI processing in favor of a chain to the previous handler at the end of my ISR?
    "Good engineers keep thick authoritative books on their shelf. Not for their own reference, but to throw at people who ask stupid questions; hoping a small fragment of knowledge will osmotically transfer with each cranial impact." - Me

  7. #17

    Default

    Quote Originally Posted by eeguru View Post
    Also is it reasonable to assume that if I chain to the previous handler, and assume other hardware IRQ routines will as well, that the last in chain will be a BIOS routine that performs a PIC EOI? ie. Is it save to remove my EOI processing in favor of a chain to the previous handler at the end of my ISR?
    Yes. For all hardware IRQ vectors, the BIOS installs a routine that does an EOI, even if it doesn't know of any hardware that uses that IRQ. It has to, because if it didn't and some unknown piece of hardware did an IRQ for which no corresponding EOI was received, the CPU would receive no more IRQs from that device. Worse, it would also receive no IRQs from any lower priority device.

  8. #18
    Join Date
    Mar 2011
    Location
    Atlanta, GA, USA
    Posts
    1,422

    Default

    Just revisited this today... I know I have serious project ADD.

    After some playing around, I dropped into a result case where the timer counts were progressing slowly but still incrementing. Which is weird - the hardware didn't change and the timer counts pace correctly with DJGPP and a PM handler. This goes back to reengnie's great observation of the stack segment overrides and Watcom's scheme of SS relativity (for lack of a better description) for near references. As he pointed out this works when 100% of the execution is in the Watcom generated context but breaks in an ISR context when the interrupt could be in anyone's back yard. Just changing all references to global variables to far references fixed most of the crazyness.

    It just seems **WEIRD**. Most code dating back to PC BIOS will do something rational like ds = cs or just making an assume that this has already been done for small/tiny models. Forget the 'it's the rational thing to do' argument for a second. It saves a tone of bytes in instruction encoding to use default segments (like data segment for data!).

    While a lot of ghosts were busted with these types of code changes, I've lost a lot of confidence in Watcom 16-bit. I can only assume the 32-bit generation is as solid as it was in the 90s but if PM was an option for this project, I'd prefer DJGPP. I really don't want to start running TCC.EXE in wine_console to do 16-bit RM development in a Linux environment But it looks like I have little choice.

    Thanks again for all the help!
    "Good engineers keep thick authoritative books on their shelf. Not for their own reference, but to throw at people who ask stupid questions; hoping a small fragment of knowledge will osmotically transfer with each cranial impact." - Me

  9. #19
    Join Date
    Jun 2012
    Location
    UK - Worcester
    Posts
    2,695

    Default

    Can you post the bit of code where count1 is declared that the HandleTimer1 function uses? I would like to know why it is referring to it via SS.

    Just wondering about how it has been declared, as to whether the compiler has been 'told' that count1 is actually stored on the stack (so it will have to refer to it via SS) but it is not smart enough to realise it can't use that mode for an ISR because the stack is no longer valid.

    Been a while, so I am just trying to reacquaint myself with the posts...

    Dave

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •