Episode 5: Plotting Our First Pixel!

Now we're going to try plotting our first pixel! This is actually pretty straightforward, but it took LOTS of reading to initially wrap my head around it. My main reference is THE reference: The IBM PCjr Technical Reference, that is. It can be a little dense, so I did plenty of Googling for specifics. A basic explanation follows.
What you see on the screen is represented by a big block of memory. That memory encodes the pixels of the screen, right to left, top to bottom, in a particular format. Every time the screen refreshes, the video hardware scans that block of memory, interprets it, and draws it on the screen. If you want to change what's on the screen, you edit that memory directly.
In the PCjr, video memory is located at hex address 0xB8000. This is not an actual physical memory location (it can't be, the PCjr doesn't support 0xB800 bytes of memory), but a virtual location which points to the actual address of the framebuffer. But how do we point to this address? The maximum value of a 16-bit register is 0xffff, not nearly enough. Enter segmented memory. All memory references are actually relative to some 'segment', which is a 16-byte block of memory whose starting hex address ends in a 0, so 0x00 to 0x09, 0x10 to 0x19, etc. Because these blocks are aligned to 16 bytes, the trailing zero is not needed, so we can actually reference the segments as 0x00, 0x01, 0x02, etc. and using this trick allows us to reference memory locations up to 1 MB (segment 0xfff = address 0xffff0, or 1048560 bytes). To reference any memory location [segment:offset]
, the segment number is multiplied by 0x10 to create the segment starting address, then the offset is added to that. As an example, if ES (the 'extra segment' register) is set to 0xB82A and SI (the 'source index' register) is set to 0x00f5, then [es:si]
references memory location 0xB82A0 + 0x00f5 = 0xB8395.
Even if memory is addressed without a segment prefix, it is still relative to a segment. Remember when we used DI in our int_to_string
procedure to write characters to a string? Those DI references are actually relative to DS (the "data segment" register), as are SI references. When we JMP to an instruction pointed to by register, that instruction address is always relative to CS (the 'code segment' register). In a .COM program, these registers are all initially set to the same value, the location where the program is loaded in memory.
So if we want to put stuff on the screen, we need to manipulate memory starting at address 0xB8000, which means we need to set one of our segment registers to 0xB800. ES, the 'extra segment' register, is a segment register that you can point wherever you choose. It cannot be set directly, only MOVed from AX or POPped from the stack, so we'll first MOV 0xB800 into AX then MOV AX into ES. Next let's set SI to 0 to modify the first byte in that segment. Finally, let's try to update the pixel(s) there by setting them to a nonzero value. I choose 0xAA. This is what we have (put it between your "press any key to continue" and your waitForAnyKey
call:
mov ax, 0xb800
mov es, ax
xor si, si
mov byte [es:si], 0aah
What does it look like?

So did we do anything? Yep, notice the top-left corner:

See the 2 green pixels? We did it!
So, why 2 pixels and why are they green?
Let's refer to our trusty IBM PCjr Technical Reference (linked at the beginning of this episode), page 84 which talks about our 320x200x16 video mode. The 16 colors are numbered (as you might expect) 0-15, which requires 4 bits (half of a byte is called a 'nibble'). Therefore each byte encodes 2 pixels, the first in the high nibble and the second in the low nibble. We have loaded the first byte of the framebuffer with 0xaa, setting the first 2 pixels to color 10, which is light green in the CGA 16-color palette.
Join me next episode when we attempt to plot multiple pixels around the screen – and run into a complication...
Completed source code for this episode is at:
https://github.com/josh2112/pcjr-asm-game/tree/episode-5