Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Controlling VGA Memory with x86 Assembly Code

DZone's Guide to

Controlling VGA Memory with x86 Assembly Code

A quick tutorial on a lesser used, but still effective, web development language, x86 Assembly Code.

· Web Dev Zone
Free Resource

Try RAD Studio for FREE!  It’s the fastest way to develop cross-platform Native Apps with flexible Cloud services and broad IoT connectivity. Start Your Trial Today!

This article is intended to make you familiar with controlling VGA with x86 assembly. Yes, we don’t hear much in the field of Assembly Language programming, as it is not quite as popular as compared to other programming and web development languages such as Java, HTML, Bootstrap, or SEM. But it is essential on its own and knowledge of it proves to be quite helpful in understanding how computers work internally. Of course, you need to be familiar with x86 assembly beforehand which includes knowing the instruction set as well as concepts of registers, operations, etc.

The setup that you will need includes installing ‘dosbox’ as well as ‘EMU8086’ on your machine. Install both of them. EMU8086 is intended to compile our source code into machine code, and then we will run the compiled code inside the environment that Dosbox will provide. After you’ve installed them, open up EMU8086, create a new ‘.COM’ file, and then paste the following code inside the editor:

org 100 h

  .data

temp dw 80 dup(0);
declares an array of 80 words and initializes them with 0

  .code

mov ax, 0xb800

mov es, ax;
es register now points towards video memory

here:

  mov si, 0

mov di, 0

mov cx, 80;
counter
for copying first line of display

;
copies the first line of the display to a temp array

loop1:

  mov ax, es: si;
character from video memory to ax register

mov[temp + di], ax;
copying that character to temp array

add si, 2;
incrementing the source and destination registers

add di, 2

loop loop1

mov cx, 3840;
counter
for shifting the remaining rows to top

mov si, 0;
destination => first box

mov di, 160;
source => box right below it

;
copies each digit from source to the memory cell above it

loop2:

  mov ax, es: di

mov es: si, ax

add si, 2

add di, 2

loop loop2

mov di, 0

mov si, 3840

mov cx, 80

;
pasting the data of the top row at bottom

loop3:

  mov ax, [temp + di]

mov es: si, ax

add si, 2

add di, 2

loop loop3

jmp here


It might feel a little bit overwhelming, but you’ll understand it by the end of the article. Actually, you can run this code inside EMU8086, but the running of this program will be very tedious and there will be no content on the screen to scroll.

My approach to scrolling the screen infinitely is to do the following:

1): The screen is 80x25 => 80 columns and 25 rows.

2): Store the first row data in a temporary array.

3): Shift the data from the remaining rows to the above rows.

4): Paste the data stored in the temp array at the end row.

5): Use an unconditional jump to go back and do the same process again.

Explanation:

The VGA screen is 80 (columns) x 25 (rows) . And each memory cell is in (2-bytes) size. So we have a total of 2000 memory cells. And in terms of size, we have 4000 bytes. So the first location is at ‘0’, the last memory cell of the first row is at‘158’ (80x2) - 2 ‘ because we start with 0. The bottom left memory cell is at ‘3840’ and the bottom right memory cell is at ‘3998.’

temp dw 80 dup(0); declares an array of 80 words and initializes them with 0.

This is the temporary array declaration inside the data segment. This will declare 80-word size memory locations inside the memory and put ‘0’ in them.

mov ax, 0xb800

mov es, ax

The VGA memory is at the address: ‘0xB800’. So we put that address inside the 'ax' register and then we move that address to ‘es’ (extended segment) register. Keep in mind that we cannot copy the address directly into the ‘es’ register.

here:

mov si, 0

mov di, 0

mov cx, 80


loop1:


mov ax, es:si 

mov [temp + di], ax 

add si, 2

add di, 2


loop loop1

We move ‘0’ to SI and DI registers and initialize our counter register cx with ‘80,' because we want to extract only the first row of the temp array. ‘mov ax, es:si’ extracts the character and puts in the ax register and ‘mov [temp + di], ax’ puts that character from 'ax' into the temp array. And we increment the SI and DI register by ‘2’ (word size) to point to the next consecutive memory location. And the loop starts again until it reaches its limit, i.e. cx is equal to 0.

mov cx, 3840 ; counter for shifting the remaining rows to top

mov si, 0    ; destination => first box

mov di, 160  ; source => box right below it


; copies each digit from source to the memory cell above it


loop2:


mov ax, es:di

mov es:si, ax


add si, 2

add di, 2


loop loop2

Now we have the first row in the temp array, and we are left with 24 rows. 80x24 = 1920 (1920x2=3840) So we have 3840 in our 'cx' register now for shifting the other rows to the top one by one. SI initially points to the first memory cell, and DI points to the memory cell directly below it that reads ‘160.’ We copy the contents of the memory cell pointed by the ‘DI’ register and paste them into the SI memory cell directly above it. And then we increment both by 2. This way we will copy the content of each memory cell to the memory cell above it until we reach the last memory cell, and that is where our loop will terminate.

Now, we have to paste the data from our temp array to the last row.

mov di, 0

mov si, 3840

mov cx, 80

;
pasting the data of the top row at bottom

loop3:

  mov ax, [temp + di]

mov es: si, ax

add si, 2

add di, 2

loop loop3

jmp here

This code will do it. DI points to the first memory cell in the temp array. And SI points to the bottom left memory cell. The counter is again at 80 (the number of memory cells in a Row) and we start pasting data from temp to the VGA memory and incrementing both pointers SI and DI by 2.

Up to Now, We’ve:

1): Copied the first row to a temporary location.

2): Shifted the rest of the rows up.

3): Pasted the data from a temporary array to the last row.

That’s when the ‘jmp here’ comes into play. This fellow will jump back to the ‘here’ label inside the code and the process will start again. Copying the new top line to a top array and then shifting all the rows up, and then finally passing the data to the last row. As ‘jmp here’ is an unconditional jump so the process is infinite. And you will see the screen scrolling in DosBox.

Running:

1): Paste the code in EMU8086. Compile it, which will give us a COM file.

2): Then open DosBox. And mount the drive with the path where your.com file resides. Eg, ‘mount c: c:\emu8086\mybuild\”

3): Shift to the newly mounted drive by entering this ‘c:’

4): Run the .com file by entering its name. Eg, ‘noname.com’

Capture.PNG

And voila.

Try pressing ‘Ctrl + F11’ the cycles in the title bar become ‘1’ . That way the program execution will drastically slow down and you will notice what is happening in the execution.

Get Your Apps to Customers 5X Faster with RAD Studio. Brought to you in partnership with Embarcadero.

Topics:
assembly ,web dev ,vga memory ,x86

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}