0 1 0 0 0 1 0 0
Or, 0x44 in
hex.
Since the default mode is text mode, I have devised a system to switch
video modes:mov r1, 1out [128], r1
This code will switch
to the graphics mode of 320x240. To switch back to the text mode, you need to execute the following
code:mov r1, 0
out [128], r1
So, number 1 at the port 128 sets the video mode to 320x240,
while 0 sets to the text mode.
The implementation in Verilog was not complicated
compared to the text mode. First of all, I had to decide which resolution to implement. I have
chosen 320x240 with 8 colors, because it consumes 38400 bytes, which is the least amount of memory
with the decent resolution and number of colors. I could not have more pixels, since that would
consume more RAM than the computer has (64KB).
Even this mode consumes more than half of the
available memory, so I can always make other modes not so demanding in memory (reduce the number of
colors). For example, having the same 320x240 black and white framebuffer would consume 9600 bytes,
or approx. 9KB.
Next, the implementation has two pixels per byte
of the framebuffer. So, I have recycled the text mode vga module and used the same two variables: x
and y to go through all the pixels of the screen. Then I had to fetch in advance the next word (two
bytes - remember, memory is organized in 32KW, having data bus 16 bits wide) containing next four
pixels. So the algorithm was simple:
- during the
visible scanline processing, the video module fetches next four pixels when displaying the third
pixel of the current word in a row
else if (x < 640 && !mem_read)
begin if ((x & 7) == 7)
begin // when we are finishing current
word, // containing four pixels, // we need to fetch in advance
// the next word (x+1, y)
// (at the last pixel of the current character,
// let's fetch next)
rd <= 1'b1; wr <=
1'b0; addr <= VIDEO_MEM_ADDR + ((xx
>> 2)+(yy * 80) + 1); mem_read <=
1'b1;
endend
- during the horizontal blanking, the video module fetches
first four pixels at the beginning of the next row
else if ((x >= 640)
&& (y < 480)) begin // when we
start the horizontal blanking, // and we need to go to the next
line, // we need to fetch in advance the
first word // in the next line (0,
y+1) rd <= 1'b1; wr <= 1'b0; mem_read <= 1'b1; if ((y & 1) == 1) begin addr <= VIDEO_MEM_ADDR + ((yy + 1) *
80); end else begin
addr <= VIDEO_MEM_ADDR + ((yy) * 80);
endend
- during the vertical blanking, the video module fetches the
first four pixels at the top left corner of the screen (start of the video memory - address 2400
decimal).
if ((x >= 640) && (y >= 480))
begin // when we start the vertical
blanking, // we need to fetch in advance the first word
at (0, 0) rd <=
1'b1; wr <= 1'b0; mem_read <= 1'b1; addr <= VIDEO_MEM_ADDR + 0;
end
When we set the addres bus to the address of the word (containing pixels) to be fetched,
then we receive that word using the following code:
if (mem_read) begin pixels <= data; rd <= 1'bz; wr <= 1'bz; mem_read <= 1'b0;end
Received pixels are stored in the pixels register.
The actual output of the pixels register to the r, g, and b wires of the vga connector
is then simple:
if (valid) begin
r <= pixels[12 - ((xx & 3) << 2) + 0] == 1'b1; g <= pixels[12 - ((xx & 3) << 2) + 1] ==
1'b1; b <= pixels[12 - ((xx & 3)
<< 2) + 2] == 1'b1;endelse begin // blanking -> no pixels r <= 1'b0;
g <= 1'b0; b <=
1'b0;end
The xx and yy variables contain actual x and y positions divided by two. x and y iterate in the
640x480 range, while xx and yy iterate in the range of 320x200:
assign xx = x >> 1;
assign yy = y >> 1;
Assembler example
Assembler examples can be found
here.
Here is the assembler example which draws two pixels, three lines
and a circle on the screen:
mov r0, 1out [128], r0 ; set the video mode
to graphics
mov r0, 0 ; x =
0mov r1, 100 ; y =
100mov r2, 7 ; white color
(0111)call pixelinc r0 ; x
= 1mov r2, 4 ; red color
(0100)call pixel
mov r2, 4 ; red
color (0100)mov r0, 50 ; A.x =
50mov r1, 50 ; A.y = 50mov
r3, 150 ; B.x = 150mov r4, 150 ; B.y = 150call linemov
r2, 2 ; green color (0010)mov r0, 50 ; A.x = 50mov r1, 50 ; A.y = 50mov r3, 150 ; B.x = 150mov r4, 50 ; B.y = 50call line mov r2, 1
; blue color (0001)mov r0, 150 ; A.x
= 150mov r1, 50 ; A.y =
50mov r3, 150 ; B.x =
150mov r4, 150 ; B.y =
150call line
mov r2, 7 ; white color (0111)mov r0, 150 ; x = 150mov r1, 150 ; y = 150mov r3, 50 ; r = 50call circle