I'm trying to learn assembly. I've written this short real mode function that works as I want to.
getndrives:
push bx
push es
push si
mov bx,0040h
mov es,bx
mov si,0075h
mov al,byte [es:si]
pop si
pop es
pop bx
ret
I would like to have the segment:offset in a constant instead and eliminate the need to push/pop es+si. Something like
biosmem EQU 0040h:0075h
mov al,[biosmem]
above code compiles but does not return the intended result.
x86 doesn't have a way to load from an immediate-constant far-pointer in one instruction. There's a far jmp ptr16:16
that can set cs:ip
to a 32-bit immediate, but for loads I don't see any option that doesn't use a segment register.
NASM docs suggest that it is/was typical in 16-bit calling conventions for DS
to be call-preserved, but that ES
could be considered call-clobbered for exactly the reason you want (in memory models that don't keep everything in a single segment).
So you could consider using a calling convention for this function that allows it to clobber ES
. (Or FS
or GS
if your code only needs to run on 386 or later.)
You can also save instructions by reusing the same register for the segment. You could have used si
both times instead of bx
, because you're done with bx
as soon as you set es
.
getndrives:
;; return in AL (zero-extended to AX; the upper byte of 0040h happens to be 0)
;; clobbers: AH, ES
mov ax, 0x0040
mov es, ax
mov al, [es:0x0475]
ret
As a bonus, you're loading into AL so it can use the special moffs
encoding of mov
that skips the ModR/M byte so it's just (ES segment override prefix) + A0 75 04
.
You can of course still push/pop es
and/or ax
around this if you want, but this is obviously clunkier. And if you're going to save/restore a segment register, @Fifoernik points out it might as well be DS to save a prefix on the mov
(unless you have any interrupt handlers that assume DS stays constant).
getndrives:
;; return in AL (zero-extended to AX; the upper byte of 0040h happens to be 0)
;; clobbers: AH, or nothing if you uncomment the push/pop of AX
push es
;push ax ; you might as well use a reg other than AX to simplify this, if you do want to preserve AH, too. And also for performance on CPUs that don't rename low8 partial regs separately, so mov al,[mem] has a dependency on the pop.
mov ax, 0x0040
mov es, ax ; or use DS if you don't need it in any interrupt handler
;pop ax
mov al, [es:0x0475]
pop es
ret
If you only care about 186 and newer, push 0040h
/ pop es
would avoid clobbering AH. (8086 doesn't have push imm8/imm16
).
In your case, you can probably avoid modifying a segment register in the first place . Notice that 0040h:0075h
is a linear address of 0475h
, which you can access with just an offset from any DS
value from 0
to 47h
. (In real mode, linear = (segment << 4) + offset
, ie left-shift by one hex digit).
With DS=0, you can still access your boot-sector code/data ( according to this PC memory map , loaded at 07C0:0
, aka 0:7C00
), and anywhere else in the low 64kiB of memory.
I don't actually write 16-bit code, so I'm not sure exactly how you'd tell NASM that you're going to set DS=0
and then have it generate offsets accordingly. But hopefully that's possible.
As a bonus, it saves code size: xor ax,ax
is smaller than mov ax, imm16
. But I guess if you were optimizing for code size, you'd push 0
/ pop ds
. (But 8086 doesn't have push imm
, that came later). And I guess if you want stack pointers to be compatible with other pointers, you'd need to mov ss, ax
/ mov sp, whatever
so that's more code.
But anyway, then your function would look like this.
getndrives:
;; return in AL
;; requires/assumes: DS=0
mov al, [0x0475]
ret
At this point it's silly to make it a function. Make it a macro, or better, or %define BIOS_BDA_ndrives 0x0475
so you can do stuff like add dl, [BIOS_BDA_ndrives]
. Or possibly %define BIOS_BDA_ndrives byte [0x0475]
to get build-time type checking, eg mov ax, BIOS_BDA_ndrives
would then fail to assemble with an operand-size mismatch.
The segment part of an address always uses a segment register. You can use a constant for the offset.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.