Page 1 of 1

How to far-jump to another section of code?

Posted: Fri Jun 15, 2018 6:50 pm
by MSDOSDudeNot
PREFACE: Hello, I usually would use programming Q&A website like Stack Overflow to ask a question like this, but I am reluctant to ask this question there. Nearly all my questions get down voted, and apparently they are off topic, even though I try my best to keep them on topic. So I hope this is a good place to ask this programming question. Also, I'm posting this here, not in the OS Development section. Even though this question is about my OS's boot loader, I do not feel that it is necessarily an OS Development question, but more so an x86 Assembly question in general.

Anyway, I'm still confused about segments, but I'm getting much better in that department, so sorry, also, if this is a noob question.

Now, I'm trying to write the boot loader so that it starts at the label 'START', and I want the CPU to far-jump to a another label, but also in the process, set the CS register to 7C00. So when it far-jumps, it sets the CS register and the IP register to the following: 07C0:(0000 + address of label) - where '07C0' is the CS register, and '0000' is the IP register that is set to the address of the label that was far-jumped towards. But I can't get it to work properly.

This is what I've tried so far:

Code: Select all

START:
; Far-Jump to the boot sector's main boot code.
	MOV	AX, 1984
	MOV	DS, AX
	MOV	ES, AX
	LEA	BX, [CHKDSK]
	JMP	FAR [ES:BX]
and,

Code: Select all

START:
; Far-Jump to the boot sector's main boot code.
	MOV	AX, 1984
	MOV	DS, AX
	MOV	ES, AX
        MOV  BX, [CHKDSK]
	JMP	[ES:BX]
and,

Code: Select all

START:
; Far-Jump to the boot sector's main boot code.
	MOV	AX, 1984
	MOV	ES, AX
	MOV	BX, CHKDSK
	JMP	[ES:BX]
and,

Code: Select all

START:
; Far-Jump to the boot sector's main boot code.
	MOV	AX, 1984
	MOV	ES, AX
	LEA	BX, [CHKDSK]
	JMP	FAR [ES:BX]
and,

Code: Select all

START:
; Far-Jump to the boot sector's main boot code.
	MOV	AX, 1984
	MOV	DS, AX
	MOV	ES, AX
	MOV BX, CHKDSK
	JMP	FAR [ES:BX]
Again, forgive me if this is an extremely noob question, and I appreciate any input.

Thanks.
Best Regards,
~ Joe

Re: How to far-jump to another section of code?

Posted: Fri Jun 15, 2018 10:12 pm
by alexfru
Something like

Code: Select all

bits 16
org 0

nop
jmp word 0x7c0:start
start:
nop
?

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 1:38 am
by iansjack
Perhaps I'm missing something, but why does it matter what value is in CS? As long as the combination of CS and IP points to 0x7C000 (which it must do if the code is running at all), what difference does it make?

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 1:53 am
by alexfru
iansjack wrote:Perhaps I'm missing something, but why does it matter what value is in CS? As long as the combination of CS and IP points to 0x7C000 (which it must do if the code is running at all), what difference does it make?
If you use non-PC-relative addressing (all kinds of addressing other than in direct calls and short/relative jumps) you kinda want to have a match between addresses in the code and in the CPU registers. It's easier to set up everything and not worry about it than to write code very carefully to work correctly in various conditions.

Specifically, if one isn't wise enough and they mix real-mode code with protected-mode code, they typically run into trouble because they don't have properly matching segment registers, code/data offsets and the GDT(R). It's one of the most common bugs. People either don't fully understand what they're doing or they just can't keep every little (and yet important) detail in their mind and so they can't see their bugs.

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 1:59 am
by MichaelFarthing
Yes, this is easily got wrong and requires some exact thinking. Alexfru's answer is by far the most straightforward for you.

However, you mght like to look at the method you were trying to use, just to see what the problem was:

Code: Select all

mov es 0x07co
mov bx start
jmp far [es:bx]
causes the CPU to look at what is at the four bytes pointed to by es:bx and jump there.

in other words it will look at the first four bytes at 0x7c00:start and instead of jumping to there will instead "load" those four bytes and then jump to the far location it has just loaded.

Thus to achieve a far jump using the form you have tried might be achieved like this:

Code: Select all

//First we need an Org statement so that the assembler calculates labels from the final intended 0x07c0:0000 base.
Org 0
//Then create a free memory location to place your desired jump destination.  I'm commandeering a bit of the stack for this
//I'm assuming there is already a reliable stack  
sub sp 4
mov bp sp
//Your memory location is ss:bp and that is where you put your jump destination [stack segment is placed in the high part
//in Intel fashion]
mov [bp] start
mov [bp+2] 0x07c0
// Now issue your far jump instruction telling the instruction to take its destination from bp
jmp far [bp]
// Note that you don't need to specify a segment (because your operand is not where you are jumping to: just the place 
// to look up where you are jumping to, which is not a far location).
As I said, Alexfru has the simplest solution.

[Edited for inaccuracies]

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 2:17 am
by MichaelFarthing
iansjack wrote:Perhaps I'm missing something, but why does it matter what value is in CS? As long as the combination of CS and IP points to 0x7C000 (which it must do if the code is running at all), what difference does it make?
If you're going to be doing a substantial amount of real code this also gives you a full 64k contiguous free space to work in before you have to worry about multi-segment programming.

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 2:58 am
by iansjack
True, but in a boot loader?

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 4:37 am
by MichaelFarthing
iansjack wrote:True, but in a boot loader?
I take the point Ian. However, I am influenced by my own situation which is a somewhat unusual project and I have indeed been constrained for space in this way (though I handled the situation by using multiple segments). Suffice to say that my challenge to myself was to develop an OS entirely within the machine using only such code as I had made available in a custom 512 vbr. The 512 boot loader was sufficient to provide a further load of an area from a (blank) partition on the boot drive and to save it back again together with an ability to edit any byte of that blank area before resaving it. It was simply a curious desire to imagine myself into the situation of the earliest developers. To be in a position to do serious development I needed a text editor, assembler and disassembler and elementary file system for the blank partition. Initial development was in machine code but that initial development gradually developed an inter-active assembler of particular instructions to ease the task. I have squeezed it all between 0x800 and 0x7c00, but only just and had to use other segments for data needs: text handling, assembler output, file system listings. In retrospect I would have used unreal mode - but as Vikram Seth said of "A Suitable Boy", "I didn't expect it to be that long".

I know it's a daft project, but it has been fun. [And you won't approve because it was all done on real hardware and I didn''t use a debugger (other than debug tricks I developed within the environment)] I have however developed backup facilities to a USB drive and can also now boot from a USB drive, so there is a reasonable level of protection.

Re: How to far-jump to another section of code?

Posted: Sat Jun 16, 2018 11:40 am
by MSDOSDudeNot
iansjack wrote:Perhaps I'm missing something, but why does it matter what value is in CS? As long as the combination of CS and IP points to 0x7C000 (which it must do if the code is running at all), what difference does it make?
I understand your confusion, so I will do my best to clear it up.

I want to, later on down the road, build my own assembler that I can use to develop the OS within the OS, if that makes sense. I don't want to add these specific directives just to maintain compatibility with my code. I can add them later, but since my bootstrapping code is as void of them as possible, it will make no difference if I add them in later, and I can still assembly my code properly.

Also, I'm trying to 'emulate', not do exactly, what the

Code: Select all

org/ORG 0x7c/C00
directive through the CPU's native instructions. I want the CS register to be set to the location that the above directive sets it to, then the IP register can go through and
start executing the code from there. That's the only reason, really. It's about having the CS register be what it would be if I used the above directive.

Re: How to far-jump to another section of code?

Posted: Sun Jun 17, 2018 2:53 pm
by Octocontrabass
MSDOSDudeNot wrote:I want to, later on down the road, build my own assembler that I can use to develop the OS within the OS, if that makes sense. I don't want to add these specific directives just to maintain compatibility with my code. I can add them later, but since my bootstrapping code is as void of them as possible, it will make no difference if I add them in later, and I can still assembly my code properly.
Which directives, specifically, are you talking about? A functional x86 assembler doesn't take much, especially if you're not concerned about convenience features like effective address algebra or optimizing for size.
MSDOSDudeNot wrote:Also, I'm trying to 'emulate', not do exactly, what the

Code: Select all

org/ORG 0x7c/C00
directive through the CPU's native instructions.
Well, there's your problem: "org 0x7c00" doesn't do anything in the CPU's native instructions. The only effect is to tell the assembler that, when it assembles your code, it should assume that the first byte will be at offset 0x7c00 relative to the segment base. You still have to write code that sets the segment registers appropriately so that the segment base and ORG offset add up to the correct physical address.

Re: How to far-jump to another section of code?

Posted: Sun Jun 17, 2018 3:25 pm
by MSDOSDudeNot
Octocontrabass wrote:
MSDOSDudeNot wrote:I want to, later on down the road, build my own assembler that I can use to develop the OS within the OS, if that makes sense. I don't want to add these specific directives just to maintain compatibility with my code. I can add them later, but since my bootstrapping code is as void of them as possible, it will make no difference if I add them in later, and I can still assembly my code properly.
Which directives, specifically, are you talking about? A functional x86 assembler doesn't take much, especially if you're not concerned about convenience features like effective address algebra or optimizing for size.
MSDOSDudeNot wrote:Also, I'm trying to 'emulate', not do exactly, what the

Code: Select all

org/ORG 0x7c/C00
directive through the CPU's native instructions.
Well, there's your problem: "org 0x7c00" doesn't do anything in the CPU's native instructions. The only effect is to tell the assembler that, when it assembles your code, it should assume that the first byte will be at offset 0x7c00 relative to the segment base. You still have to write code that sets the segment registers appropriately so that the segment base and ORG offset add up to the correct physical address.
I don't think you understood what I meant. I'm trying to do the same thing that 'org 0x7C00' would do, but through the CPU's native instructions. Of course it doesn't have such an instruction(s). (That would be cool if it did, though.) I'm also not trying to do exactly, point on the dot, what 'org' does, either. Just trying to 'emulate it.' Now as an afterthought, the 'org' directive may not set CS:IP, but regardless of whether it does or not, I just want CS:IP to be set to 07C0:(0000 + 'START'), because I think it makes more sense in my eyes. It may or may not make a functional difference, but it's just something I want to do, so I can make my system 'unique', you know.

And with regards to the directives stuff, I want to use a few directives possible, so that I can develop it outside of my operating system using, say, NASM, and can also develop parts of in the operating system, with my own assembler. And so that I can potentially use any assembler that I wish to use, since it's not using specifics to one assembler over another. And so that I can just copy-paste, or move files back and forth, and since there's as little to no assembler-specific directives as possible, I can just move the .asm files around without having to worrying about, "Oh no, did I didn't implement the directive(s) that I'm using for the assembler outside of my operating system, because it expects those directive(s), and now my assembler doesn't recognize what 'TIMES' or what 'ORG' or what 'BITS' directive(s) mean, so now I can't compile my code. (And with directives from any other assembler.)" If that makes any sense. I Hope it does. (It also makes the code simpler, believe it or not. Well, for my, anyway.)

I should also mention that I managed to do it. I did something close to alexfru's example, because it has less instructions. So thanks for the help, everyone.

Re: How to far-jump to another section of code?

Posted: Sun Jun 17, 2018 3:41 pm
by Octocontrabass
MSDOSDudeNot wrote:I don't think you understood what I meant. I'm trying to do the same thing that 'org 0x7C00' would do, but through the CPU's native instructions. Of course it doesn't have such an instruction(s). (That would be cool if it did, though.) I'm also not trying to do exactly, point on the dot, what 'org' does, either. Just trying to 'emulate it.' Now as an afterthought, the 'org' directive may not set CS:IP, but regardless of whether it does or not, I just want CS:IP to be set to 07C0:(0000 + 'START'), because I think it makes more sense in my eyes. It may or may not make a functional difference, but it's just something I want to do, so I can make my system 'unique', you know.
It makes a pretty big functional difference when it comes time to calculate addresses. Even switching to protected mode becomes complicated if you're using nonzero segment registers. Make your life easier, use the ORG statement, set the segment registers to zero.
MSDOSDudeNot wrote:And with regards to the directives stuff, I want to use a few directives possible, so that I can develop it outside of my operating system using, say, NASM, and can also develop parts of in the operating system, with my own assembler. And so that I can potentially use any assembler that I wish to use, since it's not using specifics to one assembler over another.
The syntax differences between x86 assemblers are too big for you to do that. If you're writing code in NASM syntax, it will only work correctly with assemblers that understand NASM syntax.

Re: How to far-jump to another section of code?

Posted: Sun Jun 17, 2018 5:35 pm
by MSDOSDudeNot
Thanks for your input, I appreciate it truly, but I will continue to do it the way that I initially intended, with the segment registers set to where I need them to be. I'll figure out how to do it through the way I wish to things, so that I can keep it unique, and have something that I, myself, made without copying the intellectual property of others off of tutorials and what not. I agree and know that a simple directive does not, and if so some reason, might not, count as intellectual property, but again, I want it to be unique, and to be something that I thought up, and designed by myself, since so many people use that directive. I need not say anymore on the above.

And if you saw my code, you would then understand that it makes no difference to my life, whether I use the that assembler directive, or by just far-jumping the way I'm doing it.

And I also do not mind just a little more extra work, unlike some that just want everything done the fastest way possible. I am not saying that you are one of them, but that there are some of those people out there, and I am not like them.

And adding those directives still defeats the point of me writing this assembly code to be as simple as possible, and with as little help from the assembler as possible, and to be as CPU native as possible, regardless of whether they have the same directives as one another.

Re: How to far-jump to another section of code?

Posted: Mon Jun 18, 2018 2:47 pm
by Octocontrabass
MSDOSDudeNot wrote:Thanks for your input, I appreciate it truly, but I will continue to do it the way that I initially intended, with the segment registers set to where I need them to be. I'll figure out how to do it through the way I wish to things, so that I can keep it unique, and have something that I, myself, made without copying the intellectual property of others off of tutorials and what not. I agree and know that a simple directive does not, and if so some reason, might not, count as intellectual property, but again, I want it to be unique, and to be something that I thought up, and designed by myself, since so many people use that directive. I need not say anymore on the above.
Doing things your own way is fine; my primary concern is that the nature of your initial question (about the valid addressing modes for the JMP instruction) suggests that you may also have a poor understanding of other details related to x86 assembly. Keep in mind, we get a lot of simple questions like that around here.
MSDOSDudeNot wrote:And adding those directives still defeats the point of me writing this assembly code to be as simple as possible, and with as little help from the assembler as possible, and to be as CPU native as possible, regardless of whether they have the same directives as one another.
All right then, here's a single line of code:

Code: Select all

mov ax, somelabel
What does this code do?
  1. Move the value of somelabel into AX
  2. Move the data at the location pointed to by somelabel into AX
  3. Move AX into the location pointed to by somelabel
The correct answer is, it depends on which assembler you're using. How exactly are you writing your code so that different assemblers will all produce the same result?