This is the mail archive of the crossgcc@cygnus.com mailing list for the crossgcc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: poke function ....




Dony wrote:

> Hii all,
> Right now, I'm trying to porting 80x86 code to M68K.
> In Intel, there is a function: pokeb(base_addr,offset,data)
> and using Borland C Compiler will translated like this :
>
>         /* Assembly Routine to do a pokeb                */
>         /* bp points at stack                            */
>         /* stack + 0xc = data                            */
>         /* stack + 0xa = register offset from base addr. */
>         /* es - set to be segment value (CS)             */
>         /* si - set to be register offset                */
>         /* al - data                                     */
>
>         mov ax,[base_addr]
>         mov es,ax
>         mov al,[bp+0x0C]
>         mov si,[bp+0x0A]
>         mov es:[si],al
>
> I'm a high level programmer, and I don't really understand
> where this 0x0c and 0x0a come from, and how could in 0x0c
> is data, and in 0x0a is register offset??
> Can somebody explain step by step what the assembler
> trying to do??
> Is there anybody M68K programmer know any C function to replace
> pokeb, peekb function in M68K??
>
> --
> Dony
> Email : dony@willowglen.com.sg

 Hi Dony,

     Equivalent C functions would be:

     void pokeb( unsigned char data,
                 volatile unsigned char *base_addr,
                 unsigned short offset ) {
          *(base_addr + offset) = data;
     }

     unsigned char peekb( unsigned char *base_addr,
                          unsigned short offset ) {
          return *(base_addr + offset);
     }

I'm not sure why you want to understand the assembler, but:

     When your Borland C compiler compiles the code to invoke pokeb, it
generates the following instructions:

     sub       ax,ax
     mov       al,data
     push      ax

     mov       ax,offset
     push      ax

     mov       ax,SEG base_addr
     push      ax

     sub       ax,ax
     push      ax

or something similar.  At this point, your stack looks like this:

   sp+0:       base_addr     (seg:offset form -- length 4 bytes)
   sp+4:       offset        (length 2 bytes)
   sp+6:       data          (length 2 bytes)
                              the actual data item is one byte,
                              stored in the least significant
                              byte of the word on the stack.
                              2 bytes are pushed so that the
                              stack pointer will always point to
                              an even adress.  In some of the
                              simpler 80x6 processors, writing
                              or reading a 16-bit value to/from
                              an odd-addressed word would cause
                              an exception fault)

Next, the compiler generates this:

     call     pokeb

Now, your stack looks like this:

   sp+0:     return address     (length 4 bytes -- 2 bytes for
                                 the cs segment register, and
                                 2 bytes for the offset within
                                 the cs segment of the first byte
                                 of code followinging the call
                                 instruction
   sp+4:     base_addr   (length 4 bytes)
   sp+8:     offset      (length 2 bytes)
   sp+10:    data        (length 2 bytes)

Next, in generating code for the pokeb instruction itself,
your compiler will generate the following:

   push     bp
   move     bp,sp

This is part of the prolog code generated for all standard
C functions.  It saves the frame pointer of the calling
routine, so it can be restored later on exit.  Then, it sets
up bp to point to the parameters which are currently on the
stack, so that the called function code can access them easily
The prolog code also saves other registers used within the
called function (e.g., es, si, whatever), and also decrements
the stack pointer by an amount equal to the size of any automatic
data used within the function itself.  The function parameters
are accessed as a positive offset from the new bp (frame pointer)
register, and the automatic data (pokeb doesn't have any)
are accessed as a negative offset from the new bp register.
At this point, your stack looks like this:

   sp+0:     old bp register     (length 2 bytes)
   sp+2:     return address      (length 4 bytes)
   sp+6:     base address        (length 4 bytes)
   sp+10:    offset              (length 2 bytes)
   sp+12:    data                (length 2 bytes)

The generated code you provided is:

        /* Assembly Routine to do a pokeb                */
        /* bp points at stack                            */
        /* stack + 0xc = data                            */
        /* stack + 0xa = register offset from base addr. */
        /* es - set to be segment value (CS)             */
        /* si - set to be register offset                */
        /* al - data                                     */

        mov ax,[base_addr]
        mov es,ax
        mov al,[bp+0x0C]
        mov si,[bp+0x0A]
        mov es:[si],al

The first instruction:  mov     ax,[base_addr]
loads the segment portion of the segment:offset base address into the ax
register
The offset part is assumed to be zero, and is never used.

The second instruction:  mov     es,ax
loads the segment portion of the base address from the ax register into the es
register
so that it can be used to access that segment of memory

The third instruction:  mov     al,[bp+0x0c]
loads the data byte into al.  Remember, that at this point, bp and sp both
have the same
value, and bp is normally used to access the function's calling parameters.
0x0c is the
same as decimal 12, which is the offset of the data byte on the stack.  The
data byte itself
is it offset 12, rather than 13 because all Intel machines are little-endian,
and so the
least significant byte is at the smallest offset within the word.

The fourth instruction:   mov   si,[bp+0x0a]
loads the si register with the offset value

The fourth instruction:   mov   es:[si],al
does the real poking.  The es (segment) register, and the si (offset) register
are combined
within the alu of the cpu to form a memory address, and the data byte is
stored at that
address.  Exactly how they are combined depends on the mode of the machine.
In real mode,
the memory address is calculated as ( es << 4 ) + si.  In protected mode, es
is used to
index into a mapping table (most likely the ldt) and retrieve a memory address
(24 bit for
the 80286, I forget how many for the 80[3,4,5]86).  The contents of the si
register are then
added to this value.  If virtual memory is active, this address is a virtual
address which is
then converted to an equivalent physical address using the normal memory
mapping mechanisms
before it is placed on the bus.  This last translation is invisible to the
application itself.

Hope I didn't bore you to death, but remember -- you asked the question.

Have a nice day.

Dennis
dnewbold@ivs.com