Painting pixels: Introduction to video memory Pincha aquí para verlo en español As we saw in the previous tutorial Filling the screen with colors in c and assembler (Pasmo, z88dk and sdcc), Amstrad CPC has three video modes: "Mode 0" 160 × 200 pixels with 16 colors, "Mode 1" 320 × 200 pixels with 4 colors and "Mode 2" 640 × 200 pixels with 2 colors. The video memory is located between the addresses C000 to FFFF, ie has a size of 3FFF (16,383) bytes. Depending on the mode, each byte represents 2, 4 or 8 pixels of the screen. But the video memory is linear? ie, the first byte is the upper left corner of the screen and the last byte is the bottom right?. I'm afraid not. On this subject, Amstrad CPC design left a huge surprise, which makes complicated (and fun) graphics programming for cpc . To see how video memory is structured, nothing easier than writing a program that fills it, to see how the lines are distributed across the screen. Source code for sdcc (at the end of tutorial you can download a zip) : /////////////////////////////////////////////////////////////////////// // pixel01.c // Fillin the screen with random colors // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> void main() { unsigned char *pScreen = (unsigned char *)0xC000; unsigned int nByte = 0; //SCR_SET_MODE 0 __asm ld a, #0 call #0xBC0E __endasm; for(nByte = 0; nByte < 0x3FFF; nByte++) pScreen[nByte] = (unsigned char)(rand() % 256); //KM_WAIT_CHAR __asm call #0xBB06 __endasm; } Compile and generate dsk with the following commands (pixel01.bat): sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel pixel01.c hex2bin pixel01.ihx CPCDiskXP -File pixel01.bin -AddAmsdosHeader 6000 -AddToNewDsk pixel01.dsk Run in the emulator and get this: This sequence of lines is very familiar to us if we remember the load of the display screen of many games (especially tape). As we see the lines are filled 'linearly' from left to right, but at the end of the line, instead of going to the next lower skips 8... In this table (taken from Amstrad CPC Firmware Manual) we see the organization of video memory:
The three video modes have 200 lines in height, so that these addresses are fixed for all three modes. Each of these 200 lines, is 80 bytes in size, representing 160, 320 or 640 pixels in width depending on the mode. As we said before, each byte represents 2, 4 or 8 pixels of the display depending on the mode, but to further complicate the programming, the bits of each pixel are arranged in the byte as follows:
Seeing all this information and this disorder one can only think that the person who designed this was not quite in their right mind... Practical examples:
If you look in the address table will see that there is a relationship between them, grouped in 8 of 8 lines, with breaks between them of 2048 bytes and 80 bytes for the group of 8 below. Through this relationship we can easily obtain the address of any line with the following rule: Address = 0xC000 + ((Line / 8) * 80) + ((Line % 8) * 2048) Let's apply what we just learned on the previous program, to fill the screen from top to bottom. Source code for sdcc (at the end of tutorial you can download a zip): //////////////////////////////////////////////////////////////////////// // pixel02.c // Calculating the addresses of the display // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> unsigned char *GetLineAddress(unsigned char nLine) { return (unsigned char *)0xC000 + ((nLine / 8) * 80) + ((nLine % 8) * 2048); } void main() { unsigned char *pScreen = (unsigned char *)0xC000; unsigned int nByte = 0; unsigned int nLine = 0; //SCR_SET_MODE 0 __asm ld a, #0 call #0xBC0E __endasm; for(nLine = 0; nLine < 200; nLine++) { pScreen = GetLineAddress(nLine); for(nByte = 0; nByte < 80; nByte++) pScreen[nByte] = (unsigned char)(rand() % 256); } //KM_WAIT_CHAR __asm call #0xBB06 __endasm; } Compile and generate dsk with the following commands (pixel02.bat): sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel pixel02.c hex2bin pixel02.ihx CPCDiskXP -File pixel02.bin -AddAmsdosHeader 6000 -AddToNewDsk pixel02.dsk Run in the emulator and get this: And now more difficult still, let's fill the screen pixel by pixel columns from left to right and to do so, you have to code a function to fill the corresponding bits for each pixel and color. Source code for sdcc (at the end of tutorial you can download a zip): //////////////////////////////////////////////////////////////////////// // pixel03.c // Filling the screen from left to right // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> unsigned char *GetLineAddress(unsigned char nLine) { return (unsigned char *)0xC000 + ((nLine / 8) * 80) + ((nLine % 8) * 2048); } void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel) { unsigned char nByte = *pByteAddress; if(nPixel == 0) { nByte &= 85; if(nColor & 1) nByte |= 128; if(nColor & 2) nByte |= 8; if(nColor & 4) nByte |= 32; if(nColor & 8) nByte |= 2; } else { nByte &= 170; if(nColor & 1) nByte |= 64; if(nColor & 2) nByte |= 4; if(nColor & 4) nByte |= 16; if(nColor & 8) nByte |= 1; } *pByteAddress = nByte; } void main() { unsigned char *pScreen = (unsigned char *)0xC000; unsigned int nLine = 0; unsigned int nColumn = 0; unsigned char nColor = 0; unsigned char nPixel = 0; //SCR_SET_MODE 0 __asm ld a, #0 call #0xBC0E __endasm; for(nColumn = 0; nColumn < 160; nColumn++) { nColor = (nColor + 1) % 16; nPixel = nColumn % 2; for(nLine = 0; nLine < 200; nLine++) { pScreen = GetLineAddress(nLine) + nColumn / 2; SetMode0PixelColor(pScreen, nColor, nPixel); } } //KM_WAIT_CHAR __asm call #0xBB06 __endasm; } Compile and generate dsk with the following commands (pixel03.bat): sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel pixel03.c hex2bin pixel03.ihx CPCDiskXP -File pixel03.bin -AddAmsdosHeader 6000 -AddToNewDsk pixel03.dsk Run in the emulator and get this: Finally, let's see an example, a function PutPixelMode0 complete with a bouncing pixel across the screen. Source code for sdcc (at the end of tutorial you can download a zip): //////////////////////////////////////////////////////////////////////// // pixel04.c // Bouncing pixel // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel) { unsigned char nByte = *pByteAddress; if(nPixel == 0) { nByte &= 85; if(nColor & 1) nByte |= 128; if(nColor & 2) nByte |= 8; if(nColor & 4) nByte |= 32; if(nColor & 8) nByte |= 2; } else { nByte &= 170; if(nColor & 1) nByte |= 64; if(nColor & 2) nByte |= 4; if(nColor & 4) nByte |= 16; if(nColor & 8) nByte |= 1; } *pByteAddress = nByte; } void PutPixelMode0(unsigned char nX, unsigned char nY, unsigned char nColor) { unsigned char nPixel = 0; unsigned int nAddress = 0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2); nPixel = nX % 2; SetMode0PixelColor((unsigned char *)nAddress, nColor, nPixel); } void main() { unsigned char *pScreen = (unsigned char *)0xC000; int nX = 81; int nY = 101; char nYDir = 1; char nXDir = 1; //SCR_SET_MODE 0 __asm ld a, #0 call #0xBC0E __endasm; while(1) { nX += nXDir; nY += nYDir; if(nX < 0) { nX = 0; nXDir = 1; } if(nX >= 160) { nX = 159; nXDir = -1; } if(nY < 0) { nY = 0; nYDir = 1; } if(nY >= 200) { nY = 199; nYDir = -1; } PutPixelMode0(nX, nY, rand() % 16); } } //////////////////////////////////////////////////////////////////////// Compile and generate dsk with the following commands (pixel04.bat): sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel pixel04.c hex2bin pixel04.ihx CPCDiskXP -File pixel04.bin -AddAmsdosHeader 6000 -AddToNewDsk pixel04.dsk Run in the emulator and get this:
You could download a zip with all files (source code, bat to compile, binary and dsk's) here: Painting_pixels_introduction_to_video_memory.zip |
www.CPCMania.com 2012 |