17

The base address for the video memory in MS-DOS is 0xB8000. I am trying to write to this address using debug.exe, but I am getting an error:

1165:0103 mov [b8000],ax
                   ^ Error
5
  • 1
    What you are really asking is "How do I write to video memory on an IBM PC/compatible system with MS-DOS or PC-DOS". MS-DOS did not have a set address for video memory. IBM set the standard - and most other vendors copied within a couple of years - but writing to video memory was initially just a way to speed up (by quite a bit) program output. The "correct" way in MS-DOS was to use a system call (can't remember which one at the moment) to write characters to the primary output, which almost always had the same RAM address. This complicated multitasking/windowing/etc. OS until the 80386 CPU. Commented Jun 7, 2017 at 14:25
  • 4
    @manassehkatz I'm guessing you're thinking of INT 21H AH=09H with a $-terminated string. Of course this had the pretty big downside in some situations of scrolling the whole screen after you wrote to the last character cell, making full-screen TUIs impossible to implement (though you could kinda-sorta do it by very deliberately not writing to the last character cell). That said, I think we can ignore the whole MS-DOS/PC-DOS part entirely and just focus on the question of how to do inter-segment writes in 8088/8086 assembly...
    – user
    Commented Jun 9, 2017 at 12:05
  • Note that 0xB8000 is the base address for the color adapter. For the monochrome adapter the base address is 0xB0000. Commented Dec 2, 2019 at 19:37
  • The generic way was to write a character at a time. Unfortunately the expected way to write characters was very slow, so the write-directly-to-screen-memory way very quickly caught on. For early graphics adapters care had to be taken not to get flicker on the screen (for a reason I've forgotten) Commented Dec 2, 2019 at 22:42
  • @Thorbjørn one common problem when writing directly to memory on early graphics adapters (well, CGA) is CGA snow, which happens when the CPU writes to video memory at the same time the 6845 reads from it. Commented Dec 3, 2019 at 8:55

1 Answer 1

31

You need to use a segment and offset, 0xB8000 can’t be represented directly in 16 bits:

mov ax, b800
mov ds, ax
; set AX appropriately here, or write an immediate value
mov [0000], ax

You need to go through another register to write to DS because mov ds, imm16 isn’t valid.

Note that by writing a word (AX), you’re writing a character and its attribute in one operation (in text mode).

Somewhat amusingly, mov ax, imm16 is encoded as 0xB8, so mov ax, b800 is 0xB800B8...

In monochrome video modes you’d have to use 0xB000 instead of 0xB800, and 0xA000 for some EGA/VGA graphics modes (see this table for details).

Another approach, as pointed out by tofro, is to use ES and the index registers:

mov ax, b800
mov es, ax
xor di, di
; set AX appropriately here
cld          ; ensure DI increments
stosw

This has the same effect, using stosw to write the contents of AX to the memory pointed at by ES:DI (and incrementing DI in the process, so the next stosw writes to the next character on screen). This is how one would commonly go about writing to the screen, in combination with rep and movsb or movsw to copy content from DS:SI to ES:DI. For example, here’s how to clear the screen in 80×25 mode:

mov ax, b800
mov es, ax
xor di, di
mov cx, 07d0
mov ax, 0020
cld
rep stosw

This writes 0x0020 (black-on-black space) to the screen 2000 times (80×25, 0x07D0).

14
  • 6
    @Cody there are multiple ways of going about things ;-). push imm16 requires a 286 and isn’t supported by debug, so you’d need to go through another register anyway (or manually write to the stack). I’m only preserving AX because I don’t know what comes before the OP’s mov here. Commented Jun 7, 2017 at 6:39
  • 3
    @Cody also, on slower PCs mov reg, reg is much, much faster than push reg and pop reg (2 cycles for mov, at least 19 for push / pop on 8086), and it takes the same amount of code space. Commented Jun 7, 2017 at 6:43
  • 7
    I'd rather use ES to point to video memory, as you normally have "some more program" you want to use DS for, and SI or DI to index into it, also gives you the opportunity to use fast string instructions for larger memory moves. But this might be a matter of taste.
    – tofro
    Commented Jun 7, 2017 at 6:57
  • 1
    @tofro thanks, I didn’t want to make things too complex but it is worth pointing that out (so I’ve updated my answer). Commented Jun 7, 2017 at 7:16
  • 1
    push imm16 is an instruction introduced with the 80186 not the 80286. This means it's only on 8088 and 8086 that it is not available. XT class machines using NEC V20/V30 will also support that instruction. Commented Jun 4 at 9:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .