0

I am trying to draw to screen in Real Mode, so I am trying to access 0xB8000 using segmentation

My assembly code is this

[BITS 16]
org 0x7c00

begin:
  mov ah, 0x01 ; disable cursor
  mov ch, 0x3f
  int 0x10

  mov ch, 0x0000
  mov cs, 0xb800
  mov ah, 0x0000
  mov [cs:ah], ch ; invalid effective address

end:
  jmp end

times 510 - ($-$$) db 0
dw 0xaa55

How would I address 0xB8000 using segmentation properly?

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
frogstair
  • 444
  • 5
  • 20
  • Look at [a16 addressing](https://ulukai.org/ecm/doc/insref.htm#iref-ea-a16) and [a32 addressing](https://ulukai.org/ecm/doc/insref.htm#iref-ea-a32). For a16 (which you must use on true 8086, 186, or 286) you must use `disp16`, `disp8`, `bp` or `bx`, and `si` or `di`. For a32 (which you can use on 386+) any 32-bit general purpose register can be used as a base. (In Real 86 Mode you will need to use addresses evaluating to FFFFh or less, but a32 is allowed under this constraint.) – ecm Apr 04 '21 at 14:28
  • 2
    `mov cs` is invalid by the way. `cs:` can be used as a segment prefix but it refers to whatever *code segment* you're executing in. If you want to address some other data you should use the data segment `ds` or extra segment `es`. `[cs:ah]` is an invalid address because there is no address encoding with `ah` as a register. – ecm Apr 04 '21 at 14:31
  • 2
    Note it's unwise to assume *anything* about the segment registers in a boot sector. Not even cs: the BIOS will load your code at linear address `0x07c00`, but some BIOSes will jump to your code with `cs=0` and `ip=0x7c00`, others with `cs=0x07c0` and `ip=0`. – Nate Eldredge Apr 04 '21 at 14:56

1 Answers1

6

Remember that cs is the code segment register. You can't mov into it. And even if you could you wouldn't like the results; since cs:ip is used to determine where to fetch instructions, you would find your CPU executing the contents of video memory. So you should use either ds or es instead.

Next, you can't load a segment register with an immediate; you can only load it from another register or from memory.

Finally, you can't use an 8-bit register like ah as the offset part of an address: offsets are 16 bits. In fact, the only registers that can be used in an effective address for 16-bit 8086 are bx, si, di, bp. See differences between general purpose registers in 8086. (bp addressing uses the ss segment instead of ds unless overwritten, so probably shouldn't be your first choice here.)

So a few options you could use instead:

mov ax, 0xb800
mov ds, ax
mov bx, 0x0000 ; but xor bx, bx is more efficient
mov [bx], ch
mov ax, 0xb800
mov es, ax
mov bx, 0x0000
mov [es:bx], ch
mov ax, 0xb800
mov es, ax
mov [es:0x0000], ch

As a general principle, when writing in assembly, you can't just guess as to which instructions take which combinations of operands; you have to look it up. You can only use those forms which the architecture actually implements, and sometimes their choices don't follow what you might logically assume. There are patterns which you may begin to recognize over time, but as a beginner, plan on referring to the instruction reference for every line you write.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 1
    If you're going to clobber BX with an offset anyway, you can leave AX untouched if you want by using `mov bx, 0xb800` / `mov es, bx` to set up ES or DS. Since this code isn't using AX for anything else at that point, it's not "better", but perhaps interesting to mix things up. – Peter Cordes Apr 04 '21 at 19:04
  • Although what would be better for code-size is `xor bx,bx` / `mov [bx], bl` since you already have a zero in a register, no need to init CX or CH. (It's also weird to write `mov ch, 0x0000`, because CH is only an 8-bit register.) But probably talking about that would make it a worse answer for future readers just interested in the segment-register stuff, not the other weirdness in the question's code. – Peter Cordes Apr 04 '21 at 19:09