Neutrino custom IPL problem

Hi,

I’m writing a custom IPL for the STPC processor (486 architecture), and have
a preliminary version working (actually boots and loads startup code).

However, I am having a assembly language problem, using wasm and wlink
(Watcom 6.1), and mkipl.

The processor resets to FFFFFFF0, where mkipl puts in a JMP instruction, in
my case causing a jump to F000:F990 in real mode.
My program starts at offset F990, and boots fine.

When I try to access any tables in the IPL assembly code (memory indirect),
the linker puts in the
wrong offset. For example, I have a table in the code segment,
named DefaultTable. I try to access the table as follows:

mov bx, OFFSET DefaultTable ; bx is 219, I want it to be F990+
219
mov al, CS:[bx] ; returns data from
F000:0219, Default table really is at F000:(F990+219)

The offset loaded into bx is not what I would expect (F990 + offset to
DefaultTable). In this case, bx is 219h, which is the offset from the
beginning of my program (F990), treating reset as offset 0.

Am I missing something basic about real mode programming, or is OFFSET not
working properly with Watcom?

Thanks for any help on this, I am NOT an x86 assembly language guru.

Dale

Why you don’t use the assembler stuff from gnu? I already did this kind
of stuff and I worked fine.


Bye Sascha( sascha@bitctrl.de )

Sascha Morgenstern
BitCtrl Systems GmbH
Weißenfelser Straße 67
Germany - 04229 Leipzig
Phon. +49 341 490 670
FAX. +49 341 490 67 15
eMail: sascha@bitctrl.de
WWW: http://www.bitctrl.de


Dale Pischke <dale@gyyr.com> schrieb in im Newsbeitrag:
9ainle$dm$1@inn.qnx.com

Hi,

I’m writing a custom IPL for the STPC processor (486 architecture), and
have
a preliminary version working (actually boots and loads startup code).

However, I am having a assembly language problem, using wasm and wlink
(Watcom 6.1), and mkipl.

The processor resets to FFFFFFF0, where mkipl puts in a JMP instruction,
in
my case causing a jump to F000:F990 in real mode.
My program starts at offset F990, and boots fine.

When I try to access any tables in the IPL assembly code (memory
indirect),
the linker puts in the
wrong offset. For example, I have a table in the code segment,
named DefaultTable. I try to access the table as follows:

mov bx, OFFSET DefaultTable ; bx is 219, I want it to be
F990+
219
mov al, CS:[bx] ; returns data from
F000:0219, Default table really is at F000:(F990+219)

The offset loaded into bx is not what I would expect (F990 + offset to
DefaultTable). In this case, bx is 219h, which is the offset from the
beginning of my program (F990), treating reset as offset 0.

Am I missing something basic about real mode programming, or is OFFSET not
working properly with Watcom?

Thanks for any help on this, I am NOT an x86 assembly language guru.

Dale

\

as i understand memory layout looks like the following:

jmp far F000:F900 (1)
-------------- ← FFFF:FFF0 processor startup address
— nothing—
-------------- ← F000:FFFF segment end

DefaultTable <-F000:F990+219 Default Table

some code
-------------- ← F000:F990 program startup (2)
— nothing—
-------------- ← F000:0000 segment start

so the following code should work fine:

…186

DATA_SEG = 0FF99h

code segment word public “CODE”
assume cs:code,ds:code

start:
; load data segment with program’s startup segment value
mov ax,DATA_SEG
mov ds,ax

mov bx,offset DefaultTable
mov ax,ds:[bx]

; some other code
; …

; some data
DefaultTable dw 1,2,3,4,5

code ends

end start


the idea is that address of data in segment : offset notation can be
presented in many ways. for example the absolute address of your DataTable
is 0xFF990 + 0x219 = .0xFFBA9. it can be accessed with 0xF000:0xFBA9
0xFF99:0x0219 0xFFBA:0x0009 and so on addresses. in your case you access it
defining cs: prefix → you rely on cs register value. cs register value
depends on where exactly you jumped from point (1). it can be “jmp far
0F000h:0FF90h” or it can be “jmp far 0FF99h:0000h” or many other variants.
so when you “access” your data with “mov bx, offset DefaultTable; mov
ax,cs:[bx]” in reallity you access any data withing segment space depending
on cs register vlaue set before by jump. note that assembler thinks that
offset of your data is relative to the beginning of assembled code if no
other assumptions made. so in order to write portable code and always be
sure you access right data there’s several variants:

  1. address all data with default ds segment register. this also saves a
    little of memory course directly specifying segment register differs to
    default takes an additional byte for instructions. of course it requires
    loading ds with correct value at startup as shown above. imho this is the
    best choise.

  2. if due to some reasons you need to access your data exactly with cs
    segment register prefix you must load cs with correct value. or rather in
    order to use “offset” assembler command you must be sure that cs is loaded
    with value of the beginning of your assembled code. in your case this is
    0FF99h. other way how does assembler know what additional values to add at
    “offset DefaultTable” value which is relative to the beginning of assembled
    code ? note that this case you are forced to load cs register at point (1)
    with some predefined value → some loss of flexibility.

  3. the worst variant: you can use assembler “org xxx” command. with this
    command you can specify to assembler that your code/data is located at
    additional xxx offset to the segment value. for example:

org 200h

start:
jmp near real_code
DataTable dw 1234h

real_code:
mov bx,[offset DataTable]
mov ax,cs:[bx]

…will place value 0202h in bx i.e. offset of DataTable related to the
beginning of program = 2 (jmp near takes 2 bytes) plus some predefined
offset for above case this is 200h. in general using this trick is not the
best idea course 1) you are forced to load your program at some fixed
address related to the segment value 2) for wasm offset specified at org
command cannot be larger that 0A00h. if it’s larger wasm just ignores it. so
anyway org wouldn’t help in your case.

ps: of course all above related only to x86 real mode.
pss: gas willn’t help you anyway. same troubles.

// wbr

“Dale Pischke” <dale@gyyr.com> wrote in message
news:9ainle$dm$1@inn.qnx.com

Hi,

I’m writing a custom IPL for the STPC processor (486 architecture), and
have
a preliminary version working (actually boots and loads startup code).

However, I am having a assembly language problem, using wasm and wlink
(Watcom 6.1), and mkipl.

The processor resets to FFFFFFF0, where mkipl puts in a JMP instruction,
in
my case causing a jump to F000:F990 in real mode.
My program starts at offset F990, and boots fine.

When I try to access any tables in the IPL assembly code (memory
indirect),
the linker puts in the
wrong offset. For example, I have a table in the code segment,
named DefaultTable. I try to access the table as follows:

mov bx, OFFSET DefaultTable ; bx is 219, I want it to be
F990+
219
mov al, CS:[bx] ; returns data from
F000:0219, Default table really is at F000:(F990+219)

The offset loaded into bx is not what I would expect (F990 + offset to
DefaultTable). In this case, bx is 219h, which is the offset from the
beginning of my program (F990), treating reset as offset 0.

Am I missing something basic about real mode programming, or is OFFSET not
working properly with Watcom?

Thanks for any help on this, I am NOT an x86 assembly language guru.

Dale

Ian and Sascha,

Thanks for the replies on this. One reason I’m still using Watcom is that we
are still using Neutrino 1.1 (there are reasons we haven’t upgraded to 2.x
yet).

Ian: The one thing I was trying to avoid is hard coding the start address
(i.e. FF99), as it will change each time I change the IPL code. If I could
force the linker and mkipl to start my code at F0000, the offsets should
work out, but I haven’t found how to force it to do so…

Dale

“Dale Pischke” <dale@gyyr.com> wrote in message
news:9at68p$7s1$1@inn.qnx.com

Dale,

Ian and Sascha,

Thanks for the replies on this. One reason I’m still using Watcom is that
we
are still using Neutrino 1.1 (there are reasons we haven’t upgraded to 2.x
yet).

Ian: The one thing I was trying to avoid is hard coding the start address
(i.e. FF99), as it will change each time I change the IPL code. If I could
force the linker and mkipl to start my code at F0000, the offsets should
work out, but I haven’t found how to force it to do so…

as i can see writting deep embeddable startup code is slightly different in
nto 1.1 and nto 2.11. anyway, as i understand your code is divided at least
on two parts: startup code at upper memory addresses where CPU jumps after
reset signal and main initialization code located somewhere else. usually
first is an important part of initialization at least here you can tune
initial memory access conditions like mapping memory banks & etc but this is
a nonsignificant part from code size point of view. as usually after initial
CPU/memory conditions are tuned ok you can jump on predefined main
initialization code address and complete all needed operations including
starting up of os kernel and so on. i guess both parts of initialization
code depend on target system’s exact hardware configuration so usually are
written by hands for each system and at least you’r free to implement it as
you wish and can do about anything even that’s impossibly :slight_smile: as i understand
you selected this way…

i didn’t find any link on “mkipl” utility in nto 2.11 documentation so guess
smth was changed during 1.1 → 2.11 transformation. also i didn’t find docs
for nto 1.x so don’t know what this utility supposed to do :frowning: anyway, let’s
suppose you’r doing all by hands i.e. startup code and at least first part
of initialization code.

i understand your wish to avoid using hard coded addresses but what do you
mean asking linker and mkipl “to start up your code from F0000” ? when you
execute jmp [0F000:0000] (not exact notation) from your startup code you
already starting up executing your second part of code at F0000 address. but
the problem is that you cannot access at this point data located somewhere
in initialization code segment with offsets precalculated by assembler or
compiler, right ? this is course assembler during code processing assumes
that segment register will be loaded with correct value before actuall data
access and all data offsets are calculated relatively to this loaded segment
value. in your original message you told that startup code jumps at
F000:F990 address. no problems. with this jump you load your CS:IP registers
pair with correct code address and should execute it ok course you specify
absolute jump address. but inside initialization code calling “offset”
command and accessing data with CS register prefix assembler calculates all
offsets relatively to Seg:0000 address, F000:0000 your case. there’s no F990
offset addition ! and afaik there’s no elegant ways to force watcom wasm
assembler to take into consideration that it must add value F990 to all
offsets returned by “offset” command calls. sometimes “org xxx” command
helps i.e. it does exactly this - adds some predefined value to intermal
offset counter, but it’s limited with maximal A00 offset value, at least
afaik. and anyway, even if you could use “org 0F990” i.e. force the
assembler kind of “to start data from F0000” you automatically use that
evil hard coded data offset addition and every time you change location of
your initialization code in memory or rather data offset is changed
relatively to used segment you must fix this value and reassemble + relink
the whole code. this is not you want, right ? :slight_smile:

note that in your startup code you anyway must know where to pass control
i.e. where your initialization code is located, so you cannot except hard
coded addresses at all. of course you can do smth like autolocator of
initialization image but usually size of startup code is highly limited and
in general this is not so trivial task. so would be better to do smth like
this:

  1. place your initialization image somewhere in linear executable memory at
    paragraph (16 bytes) aligned address. F000:F990 = FF99:0000 is fine.

startup code:

  1. do all needed minimal CPU/memory turnings.
  2. load in DS segment value of initialization image address. FF99 in your
    case.
  3. execute jump at absolute address of yout initialization code.

initialization code:

now at this point you have already correctly loaded DS segment register with
so you can easily access any data inside your initialization code/data
segment also with “offset” command. i.e. mov bx,offset DefaultTable, mov
ax,[bx] will work fine. note that you accessing data with default DS segment
address part so don’t specify CS: prefix.

advantages of this way: you don’t have to deal with absolute addresses
inside your probably huge initialization code. all needed information is
passed at startup with DS register so there’s no dependency where exactly
it’s located → you can move it freely as required (note step0). in order to
move it you just have to change one -two defines inside startup code which
supposed to be very simply and easy to maintain. also no need to rebuild
image every time you move it at some new absolute location in memory. don’t
forget that if you want to access data with some another segment register
for example with ES you must load it with the same value as DS. also you
must load correclty SS:SP pair in order to use stack instructions.

well, seems like this response is slightly big but hope it helps a little :slight_smile:

Dale

// wbr