0

I'm writing a multi-modular assembly project with MASM for DOS. In particular, I'm trying to do a very simple DOS-like OS,

I have several segments defined like this;

fileseg segment byte public 'code'
fileseg ends

memseg segment byte public 'code'
memseg ends

diskseg segment byte public 'code'
diskseg ends

bootseg segment byte public 'code'
bootseg ends

As you see all of them are byte-aligned (design decision). Also I have a cseg group that encompasses all of the above segments

cseg group fileseg, memseg, diskseg, bootseg, ...

so when I want to reference a public label, no matter if it's defined in a different segment, i will do something like:

assume ds:cseg, es:cseg, ss:cseg

mov   si, offset label_in_this_segment
mov   bx, offset label_in_another_segment

and it'll get the 'global' offset within cseg, which is right. But the problem arises when you write some code that needs to be executed in a fixed location, as a boot sector, that executes at 0000:7C00h (in my case at 07C0:0)

so I wrote it like this:

bootseg segment byte public 'code'

; Want all data references to be relative to this segment, not CSEG
assume ds:bootseg

boot_start::

    jmp     start

    boot_data   t_bootloader_data <>        

start:

    ; make IP point to 0000h
    mov     ax, 07C0h
    push    ax
    mov     ax, offset ds:real_start ; "BAD" offset
    push    ax
    retf
real_start:        
    mov     ax, cs
    mov     ds, ax
    ...

I put "BAD" in quotes because MASM is actually doing it right, since bootseg is also part of the cseg group (because boot_data it's referenced from some other segments), so the linker marks "real_start" as relocatable.

So the question would be: how do I tell the assembler that I wantreal_start to be offseted from bootseg itself?

I tried some tricks that work, like making bootseg be the first segment defined, or making it paragraph-aligned, but these seem somewhat hacky to me.

I also tried to put bootseg out of the cseg group but again, I need boot_data to be referenced from other segments before the boot sector is written to disk.

Trap
  • 12,050
  • 15
  • 55
  • 67
  • 2
    The problem: the bootseg has two values: at boot, and after long jump. You could do a offset difference. (which is valid and the same on both cs) and use such value. – Giacomo Catenazzi Apr 12 '18 at 13:16
  • Don't try to get MASM to understand the offset. Maybe do something like `jmp 07C0h:(real_start - start)` if that's the right syntax (instead of that push/push / retf stuff. IDK why you using AX for that if you're not going to take advantage of the value still in AX after reaching `real_start`.) – Peter Cordes Apr 12 '18 at 13:48
  • @PeterCordes It's compiled for .286 so can't neither write jmp XXXX:XXXX nor push immediate values. – Trap Apr 12 '18 at 14:42
  • 2
    @Trap The 80286 most definitely has the `jmp far ptr16:16` instruction. Yes, you can use it. – fuz Apr 12 '18 at 15:02
  • [`push imm8/16` is 186](https://www.nasm.us/doc/nasmdocb.html). `jmp ptr16:16` is baseline 8086 according to the same doc. – Peter Cordes Apr 12 '18 at 15:22
  • @PeterCordes You're right, you can do push imm. However, despite jmp xxxx:xxxx is a valid instruction, MASM doesn't seem to like it. I always have to write some kind of workaround, like inlining a db 0EAh, followed by a couple of dw's for seg:off, in which case, I prefer to do the push/push/retf stuff – Trap Apr 12 '18 at 16:07
  • Maybe check the manual and find out the syntax; Maybe they want `jmp far something:something`, or maybe there's a different syntax altogether. – Peter Cordes Apr 12 '18 at 16:22
  • 2
    Without looking too closely at your question if it solves your actual problem, the direct answer is use `OFFSET bootseg:real_start`. As for how to get MASM to generate a far jump to specific fixed segment and offset see this question: https://stackoverflow.com/questions/32706833/how-to-code-a-far-absolute-jmp-call-instruction-in-masm – Ross Ridge Apr 13 '18 at 05:26

1 Answers1

2

You can specify what segment you want OFFSET to be relative by prefixing the symbol with segment:. For example:

     mov     ax, offset bootseg:real_start

However this doesn't work because you're using BYTE alignment, so the there's two different starting address for the segment. In real mode segments have to be paragraph (16-byte) aligned, so linker rounds down the start bootseg to the nearest paragraph boundary, and then adjusts any offsets to be relative to the new starting address. Since when the bootseg is actually loaded and executes it does actually start a paragraph boundry this means all the adjusted offsets are incorrect.

Note that is isn't limited to just the OFFSET directive, but also any absolute references bootseg makes to addresses within bootseg. You should really consider making bootseg PARA aligned.

If you still want to use byte alignment you'll need to calculate the offset yourself:

     mov     ax, real_start - boot_start

Note that since you're targeting 80286 processors, you can do this instead:

     push    real_start - boot_start

The PUSH immediate instruction was introduced to the x86 architecture with the 80186.

If you have absolute references like this within bootseg:

mov  al, [boot_data.foo]

You'll need to change it to something like this for the offset to be correct when the boot sector is executed:

mov  al, BYTE PTR ds:[boot_data.foo - boot_start]

Finally you can avoid the whole PUSH/RETF nonsense and code an absolute far JMP to a fixed segment directly in MASM like this:

bootseg segment byte public 'code'

assume ds:bootseg

boot_start::

    jmp     start

    ; ...

start:

    jmp     real_start_abs
real_start:        
    mov     ax, cs
    mov     ds, ax

    ; ...

bootseg ENDS

bootseg_abs SEGMENT USE16 AT 07c0h
    ORG      (real_start - boot_start)
real_start_abs LABEL FAR
bootseg_abs ENDS
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • The segment prefix 'bootseg' does the same than 'assume ds:bootseg' and then ds:xxx_label. Tried both and neither of them worked (yields the same "invalid" offset) – Trap Apr 13 '18 at 17:00
  • The problem has to do with bootseg being part of a byte-aligned segment group. – Trap Apr 13 '18 at 17:02