#1 - Hello world! (part 1 of 2 - basic knowledge)

So, this is the first real post on MSX machinecode programming. I'm afraid it will be quite long as you do need to have some basic knowledge before you can start programming. You really need to know something about:
  • The parts of a MSX-computer
  • Numbers and the number system
  • The Z80 processor
I'll try to keep it as short as possible, but still understandable. At the end of this blog we will have a working program that writes "Hello world!" on the screen.  
Oops, sorry, not true. This blog got so long that I've decided to split this up into 2 parts: "basic knowledge" and "programming our 'Hello world!' "  
I promise that part 2 will be there very soon!


But first a tip!
I'm trying to make this blog as good as possible, but there are many other sources where you can find information and I want to encourage you to use them. So find yourself websites, PDF-documents, textfiles, Youtube-video's, books and so on. Get all the information you can find and do a lot of little experiments. Try, try, try and try again. Find even more sources when you don't understand something and post questions on forums to ask other who do understand it. Have a look at programming code of others, try to understand it and change it. All of this will make you a better programmer. 

About the MSX

Let's have a look at the most important parts of the MSX-computer that we need to know about. The heart of our computer is the Z80 microprocessor. This is a chip that can perform (simple) arithmetic and logic operations. It can access the memory in the computer, handle input from a keyboard or joystick and produce output by controlling the video and audio chip (and it can do many other things too...). The programs we are going to write will be read and executed by the Z80. There is in fact no MSX machine code, what we will learn is Z80 machine code written to be executed on a MSX computer.








The Z80 is connected to memory. Inside the MSX are 2 types of memory:
  • ROM - Read Only Memory
    This is memory that can't change, it's fixed. It contains the MSX BIOS (Basic Input/Output System) that initializes and boots the computer, but also contains lots of handy parts of machinecode that we can use in our own programs. The ROM also contains the MSX-BASIC interpreter which ensures that we can use the BASIC language to program our MSX.

  • RAM - Random Access Memory
    This is memory that can be changed. The Z80 can read from it and write to it. This is where we will store our programs, our images, sounds and variables like player positions and highscores. There's also data from the BIOS and the BASIC interpreter here.
    Keep this in mind: once we turn off our computer everything in RAM is gone... If we want to keep data that's inside RAM we have to store it, for example on a floppy disk or a hard disk (or like the old skool way from the eigthies... on a data cassette!).
A Z80 that runs on it's own is no fun. We want to give it some input and we want to get some output too, to see or hear things that are happening!
In most programs and games we can use the keyboard or joystick to give our MSX some input.
For the output we have:
  • The VDP - Video Display Processor
    This is a videochip that has it's own part of RAM, the so called VRAM (VideoRAM) where characters, images, sprites and so on are stored. The VDP can send images to a tv or monitor that is connected. The Z80 can not directly access the VRAM of the VDP, but we'll cover that later on. 

  • The PSG - Programmable Sound Generator
    This is a soundchip that can play tones or noise to to three separate voices. Don't expect miracles, but it's good enough for some game music and sound effects.
This is a rough, general and incomplete overview of what's inside a MSX computer. Just enough to get us started. I'll provide more information when it's needed.




The number system: bits, bytes, binary and hexadecimal

Humans are used to the decimal system nowadays. We've got 10 digits (0...9) that we use to represent our numbers. Therefore it's a base-10 number system. Unfortunately this is not the most convenient system for computers. 

Please note! When I refer to a decimal number below, I always mean a whole number from the decimal system and not a number with a decimal point!

Computers use bits to store information. You can compare a bit to a lamp. A lamp can be on or off: it has 2 states. In the same manner a bit can be 'on' or 'off'. When it's on it has a value of 1 and when it is off it has a value of 0. This allows information to be stored in a bit.
You might think: what can you do with just a value of 1 or 0? Well, we will learn that sometimes 1 bit is just enough. For example if an image on the screen should be visible. A value of 1 could mean visible and 0 could mean not visible. In this case 1 bit is just fine.

For many other occasions 1 bit is not enough an therefore 8 bits together are combined to a byte. A byte may look like this:

10011000

For humans this is not very pleasant to read. Luckily we can convert these 8 bits to a decimal number. 

  • The bit completely on the right is called the Least Significant Bit (LSB). It represents a decimal value of 1 or 0.
  • The bit 1 place to the left of it represents a decimal value of 2 or 0.
  • The next bit to the left represents a decimal 4 or 0, and so on.
  • The bit on the far left called the Most Significant Bit (MSB) and represents a decimal 128 or 0. 
In the example byte it would look like this:



 

 
And we can calculate the value of this byte in this way:

1 x 128 = 128
0 x  64 =   0
0 x  32 =   0
1 x  16 =  16
1 x   8 =   8
0 x   4 =   0
0 x   2 =   0
0 x   1 =   0 +
          152

So the value of the byte 10011000 is in our decimal system 152. That's much better to understand,

Bytes can have 256 different values, from 00000000 (decimal 0) to  11111111 (decimal 255).
Representing numbers with bits is called the binary system. It's a base-2 number system.

But what if we need bigger numbers? Well, then we combine two bytes (or even more if necessary).
These two combined bytes can have a value from 00000000 00000000 (decimal 0) al the way up to 11111111 11111111 (decimal 65535). Two bytes combined even have a special name, together they are called a word.

And what about negative numbers? Or numbers with a decimal point? They can be represented by bits and bytes as well. But we will skip that for now.



Confused? Well, don't worry! The good thing is that there are calculators, programs and websites that can convert binary digits to decimal and vice-versa. There's no need to do this by hand, but you do have to understand how it works. The most important things to know by now are:
  • Computers store information in bits
  • A bit can have a value of 0 or 1
  • 8 bits make a byte
  • A byte can have a value from 0 to 255
  • 2 bytes make a word
  • A word can have a value from 0 to 65535
Ok, take a break first if you need it. Or read it again for better understanding. Then proceed, because we're  not done yet with number systems.



Besides the decimal and the binary system there's also another number system that you will use a lot. It's called the hexadecimal system and it's a base-16 number system.

The hexadecimal system has 16 different digits. But as there are only 10 different digits in our decimal system the letters A, B, C, D, E and F are also used (they may be written in lowercase too). Let me show you how this works:


decimalhexadecimal
00
11
22
33
44
55
66
77
88
99
10A
11B
12C
13D
14E
15F

Once we get to 16 we have no more hexadecimal digits left. So just like in our decimal system we have to combine 2 digits. Like this:


decimalhexadecimal
1610
1711
......
2519
261A
......

The hexadecimal value 19 has a decimal value of 25. This can be calculated in this way:

1 x 16 = 16
9 x  1 =  9 +
         25

And as another example, the hexadecimal value CF has a decimal value of 207:

12 x 16 = 192    (C=12)
15 x  1 =  15 +  (F=15)
          207

Don't worry if you don not fully understand it right now. There are a lot of converters for the hexadecimal system as well!

But why is the hexadecimal system so convenient to use with computers? Let's look at the values of a byte:
  • The lowest decimal value 0 is also a hexadecimal 0
  • The highest decimal value 255 is the hexadecimal value FF
FF perfectly fits the highest value. It's like 99 in our decimal system. Now let's have a look at 2 bytes combined, also called a word:
  • The lowest decimal value 0 is also a hexadecimal 0
  • The highest decimal value 65535 is the hexadecimal value FFFF
FFFF also perfectly fits. And what's also very useful is that you can easily split a word in 2 bytes. Let's for example take the decimal number 22238. This can be converted in the hexadecimal number 56DE.
When the Z80 works with this number it in fact uses the separate bytes 56 and DE as it is an 8 bit processor. Sometimes you'll find it very handy to know what the separate bytes in are when you're writing your programs. This is very easy with the hexadecimal system.

But how do we know if 10 is decimal, binary or hexadeximal? They are after all written exactly the same, but have completely different values That could be a problem and that's why there is a special way of notating binary and hexadecimal numbers. Unfortunately, not every program uses the same convention, but don't worry: it will be not too confusing or difficult. For an example, let's have a look at MSX-BASIC:
  • 10   = decimal
  • &B10 = binary
  • &H10 = hexadecimal
In this way it's always clear what number system is meant.


 




About the Z80

We're almost there. Let's talk about how the Z80 works. This is after all the chip that will execute our programs. Roughly it goes something like this:
  • The Z80 is connected to the memory. A part of the memory is filled with the bytes that form our program. We might have loaded it from a disk. 
  • The Z80 reads 1 byte from memory at the point where our program starts.
  • This byte is recognized by the Z80 as (the start of) an instruction. It might be necessary to read some additional bytes that are needed.
  • The Z80 executes the instruction.
  • The Z80 reads the next byte or bytes, executes the instruction, and so on.

Registers

To understand the instructions we can give to the Z80 we first have to know what registers are.
The registers of the Z80 are internal memory of the processor. There are 8 bit registers that can store 1 byte and 16 bit registers that can store a word (2 bytes). Some of these 8 bit registers can be combined to a pair and in this way such a register pair can also store a word. Some of the registers have a specific function and some are for general purpose. Let's have a look at the most important registers for now. Be aware that there are some more.




PC - Program counter - 16 bit
The program counter has the size of a word. It keeps track of the place of the next instruction in memory that the Z80 has to execute. The maximum value of a word is 65535 and so that's why the maximum size of memory that a Z80 can address is 64 kilobytes. Luckily there is a way to 'switch' between parts of the memory and therefore a MSX can have more then "just" 32 kilobytes of ROM and 32 kilobytes of RAM.
The PC (program counter... not a personal computer...) automatically increases when our program is executed. We don't change the PC ourselves by assigning a different value to it. The PC also changes when we use an instruction to jump to another place in memory.

A - Accumulator - 8 bit
The accumulator is a very important register. It is used for the most arithmetic and logic operations in the Z80. You'll be using this register very often.

F - Flag - 8 bit
The separate bits of the flag register indicate the status of the result of calculations or certain instructions. One of these bits is the zero flag. It can for example tell us if the result of subtracting two numbers was zero. This can be used to make decisions in our programs. There are other flags as well which we will cover when we use them.
The flag register also forms the register pair AF with register A.

B and C -  8 bit or 16 bit as a pair
These registers can be used separate or as a pair (BC). They are for general purpose and they are often used for loops.

D and E8 bit or 16 bit as a pair
These registers can also be used separate or as a pair (DE) and are also for general purpose. Besides that they can also be used to copy data from one place to another in memory.

H and L  - 8 bit or 16 bit as a pair
Yes... you guessed it.. these registers can also be used separate or as a pair (HL). Again, they are for general purpose, but HL as a pair is often used to address a place in memory.









SP - 16 bit
This is the stack pointer. The stack is a place in memory where small amounts of data (2 bytes at a time) can be temporary stored. This is for example used for a special kind of jump when the program counter later on needs to return to a specific memory address. The return address that has to be remembered is temporary stored on the stack.
You can also store information on the stack by yourself. It works like a real stack: the last thing you've put on the stack is the first thing you have to take off it. Last in... first out... And don't forget to take off what you've put there!

The stack pointer keeps track of the address of the top of the stack. This is automatically done for us and we won't probably ever try to change the stack pointer by ourselves. The stack actually grows from top to bottom in memory, but let's not worry about that for now: the principle is the same.

IX and IY - 16 bit
These are index registers. The come in handy if you want to point to an address in memory were you want to read or write a certain amount of bytes. They are often referred to as 16 bit but are in fact register pairs containing IXH (high byte) and IXL (low byte) and also IYH and IYL. 











The Z80 instructions

And finally there are the instructions of the Z80. There are not that many and each of  them can only perform a small task. So we will be needing a lot of instructions to get things done. The instructions can be divided into a number of groups and the most important are:
  • instructions to load data into or from registers or memory,
  • instructions to perform arithmetic and logical operations,
  • instructions to handle the bits in a byte,
  • instructions to compare bits and bytes,
  • instructions to jump to another place in memory, 
  • instructions that handle input and output of data, for example to or from the video chip (VDP).
There a few more but that's about it. No instructions to print something to the screen, to open a file, or to... Just simple instructions that we have to combine to get something done.

Let's give an example. What if we want to "fill" the register A with the decimal number 10. Then we need to give the Z80 this instruction:

LD A,10

This stands for: load the value of 10 in register A.  But as our Z80 does not understand any of this we have to "translate" our instruction to the following 2 bytes:

3E 0A

The Z80 first reads the byte with hexadecimal value 3E. This value 3E stands for the the instruction LD A,n where 'n' is value to be put into register A. After that it reads the next byte out of memory: 0A that is placed directly after the instruction 3E (keep in mind that hexadecimal 0A = decimal 10).

And now the Z80 'knows' what to do and also has al the information it needs. Register A gets a decimal value of 10 and the Z80 proceeds by reading the next byte out of memory.

And so this is what our little program looks like in memory:




Assemblers!

What you've just seen is actual machine code or machine language. Bytes with values that stand for instructions or data. You could actually write a complete program with a so called hex-editor if you wanted to. A hex-editor allows you to enter hexadecimal numbers byte by byte and save it as a file that you can load into the memory of your MSX.
Or you could use the POKE command in BASIC to fill the memory with all these bytes. You can find all the Z80 instructions and the corresponding codes (so called "opcodes") in the manual of our processor, so go ahead!

Well... even for most nerds that's a bridge to far. So luckily there are programs that can do this translation work for us. They are called assemblers and they can convert (assemble) our Z80 instructions into the bytes that a Z80 understands. And besides that, a assembler has all kind of other functions that makes it (a lot) easier to produce machine code. 

So just to know: we will be writing our program in assembly language, with instructions like LD A,10.
This does not add all kind of 'overhead' to our machine code that could slow it down. It's just a more understandable way for us humans. Have a look at this simple and not very useful program (you don't have to understand what it does):

At the right side are the instructions in assembly language. You also see the word "LOOP". This is a so called lable that here helps to jump to a certain place in our program.
On the left side are the actual machine code bytes. You can see that "LOOP" does not add any bytes to our program. Even not when it is placed after the DJNZ instruction. This is a jump instruction that always takes up 2 bytes in memory. The thing the assembler does here is that it calculates the right value for us. Something that we would otherwise have had to do ourselves. So there's nothing more and nothing less.

Hey! What about our "Hello world"?

Ok, I've just decided that this is enough for one blog. I will split it up in two parts and in the next one we will programming our "Hello world!' program. I will post it soon (promise!).

If you really can't wait for that and are desperate for more information right now than have a look at this:

  • Z80 Assembler for Dummies on msx.org.
    This website is a real good source for information and also has a very active forum about developing for MSX computers. The article provides additional information to what you've just read.

  • Youtube video's and a website about programming the Z80 for absolute beginners by ChibiAkumas. He also wrote a book about it.
    Lots of information here, so have a look!

That will keep you busy for now ;-)


Any comments or questions? Leave a message!


Reacties

Populaire posts van deze blog

#3 More on BIOS calls for the text screen

#2 Hello world (part 2 of 2 - programming)