A Little Background Story
Back in 2013 I wanted to learn how to build a homebrew computer, like what hackers and hobbyists did back in the 1970s during the homebrew computer craze. Although I was about 40 years late, I wanted to make a homebrew computer of my own. Sure, there are much better computers nowadays, but unless you can make circuit boards that have REALLY small solder connections, you can’t really make your own from scratch. Dual in-line package (DIP) integrated circuits (ICs) are really where it’s at in DIY hobby electronics, because you can easily insert them into solderless breadboards. That’s why the majority of hobbyists prefer DIP ICs.
I didn’t know much about the internals of computer systems when I started, so I had to get some books on them. One of the first books I got was The 8088 Project Book by Robert Grossblatt. It’s an extremely good book for beginners, and I particularly enjoy the “Laws of Life and Design” which he advocates (humorously?) living by.
So after I had read and reread, and reread, and reread portions of the book over and over again, until it started to make sense, I went over to Jameco and bought some parts. My assembly wasn’t the greatest when I started out on this project, but thanks to the wizards and Real Programmers over at Vintage Computer Federation, it’s coming along.
I had abandoned the project when I couldn’t pin down a bug, but I decided to give it another try. Luckily, the LCD display you see in the picture above was still hooked together. Thanks to Grossblatt’s skill at explaining things and a few things I picked up along the way on my own, it didn’t take me long to refresh my memory. In short time I got right back to where I left off.
What Grossblatt had in mind when he wrote the book was to make a CPU-based controller, which could be used to automate any number of things. As a result, he only designed the system for 2KB of ROM and 2KB of RAM. However, what I want is a full-fledged computer. Right now I only have the 2KB of ROM and 2KB of RAM on my board, but I plan on upgrading it later.
The 8088/86 has 20 address lines which allow the CPU to address up to 1MB of memory. When power is first applied to the 8088, obviously it has to look somewhere for the first instruction to execute. This location in memory is called the reset vector. Sometimes it’s referred to as the power-on, or power-up location. When the 8088 starts up, the CS register is initialized to FFFFh, and IP is initialized to 0000h. This spot is at absolute memory address FFFF0h, which is 16 bytes below the end of memory. That doesn’t leave much space to store anything, so an intersegment jump, or a far jump, is placed there, to cause execution to resume at a lower place in memory.
The 8284 Clock Generator
At the heart of everything is is the 8284 clock generator, which was intended to provide neat clock pulses tailored specifically for the 8088. You could also use simple crystals and oscillators, but the 8284 provides a convenient reset signal to the 8088, among other things. The standard configuration is a 14.31818MHz crystal connected between pins 16 and 17. The 8284 divides that frequency by three, providing a constant clock at 4.77MHz on pin 8. It also provides a peripheral clock, which is half the system clock at 2.38MHz.
As it is right now I don’t have any input, and the only output I have is an SN74LS373N octal latch, which drives eight discreet LEDs to display crude binary. The I/O ports are handled by the 8255 programmable peripheral interface (PPI). Originaly, I planned to connect a PIC16F690 microcontroller to one of the 8255’s three output ports (which is what is controlling the LCD display in the pictures), and connect my LCD display to that. However, I’ve decided that I’m going to use the 8237A Direct Memory Access controller to allow the PIC to directly access memory. Note: the first 128 bytes of memory are reserved. The author of the topic linked to here provided a link to the intel 8088/86 manual.
My plan right now is for the 8088 to write the text to be displayed to the first 80 locations after the 1KB interrupt vector table, which is how many visible characters the LCD module can display simultaneously (80). For some reason I thought I remembered there being a capacity in the module’s memory for more characters than what it could display, but I just checked a datasheet for it, and it said its capacity was 80 characters. If anyone can find a source validating my thought that its capacity is beyond 80 chars, please post it in the comment section below. The reason I want to use a microcontroller to take care of the ASCII text in memory is to free up CPU time.
This video was from my previous time building a homebrew computer, and right now I’m at the same point the one in the video is, only this time I didn’t bother putting the 7-segment LEDs on it, because I have something better in mind, the LCD display.
Here is the source code (NASM):
; ************************************************ ; * * ; * A TEST PROGRAM TO PRACTICE TIMING LOOPS * ; * * ; ************************************************ ; section .data ROM_SIZE equ 2048 ; size of ROM org 0x100 ; Set the origin to match IP at ; 0x100. This makes it ; compatible with DOS section .text start: mov sp,0x0800 ; Initialize the stack pointer ; to 0x0800, which is 2KB ; from the lowest point in ; the memory map mov al,0x90 ; This sets the 8255 to operate ; in Mode 0 (basic input ; output) with port 0 as an ; input and ports 1 and 2 as ; outputs. out 0x03,al mov bx, ss ; Initialize BX to 0, since SS ; hasn't changed yet jmp begin ; Jump to the main loop. waitasec: mov word [bx],0x6FFF; Reset the countdown timer. waitasec1: dec word [bx] ; Decrement it by 1 each time. out 0x01,al ; Display the hex value at the ; 8255's port 1 on the LEDs. cmp word [bx],0x00 ; If the timer has counted down ; all the way, return to the ; 'nexthex' label so that it ; can move on to the next hex ; value to display on the LEDs jnz waitasec1 ; If the counter hasn't counted ; down to 00h yet, keep going ret ; Here is where the zero flag ; is checked. If the timer ; has reached 00h, go to ; 'nexthex' begin: mov al,00000000b ; Set all eight bits to 0. call waitasec ; Start the countdown timer. nexthex: inc al ; Go on to the next hex value. call waitasec ; Show the hex digit for a while. cmp al,0x2F ; We're back in 'nexthex' again, so ; we check to see if the ; second LED has reached the ; maximum value. jnz nexthex ; If not, move to the next hex ; digit to display. jmp begin ; If we have reached the last ; possible value in the ; character table, start all ; over again. times ((ROM_SIZE-16) - ($-$$)) db 0 db 0xEA ; far jump dw start ; Sets the offset IP value dw 0x10000-(ROM_SIZE/16)-0x10; Target CS value times ROM_SIZE - ($-$$) db 0