Monday, 23 January 2012

It's funny how things turn out sometimes. I had what I thought would be a good, solid game concept and tried to implement it on a Sinclair ZX81 in 16K, and, well it was a little bit more dull than I imagined it, but then as I was using the z88dk again, there were some compromises that I had to make mostly due to speed. Too much on the screen would have made proceedings too slow and boring anyway, so I decided to limit the maximum number of ships in a convoy, bombs and bullets. I also made the screen smaller and set a limit to the distance that each bullet will travel on the X and Y plane. I'm sure any pure Z80 coders could make a better implementation.

It's available from the RWAP Software website, and the source code is on pastebin here. Any comments, questions or feedback is welcome.

Tuesday, 10 January 2012

Over the past week or so, I've been playing around with C and the z88dk (z80 development kit), and I've finally got something stable enough to run on an emulated Sinclair ZX81 +16K, which I released yesterday and can be downloaded from here - I recommend the EightyOne emulator (just search for it). If you want to see the source code, a partially commented version is posted on pastebin. Bounce is freeware, by the way.

The great thing about programming for a limited platform - and they don't come much more limited than the old ZX81 now, do they? - is that it forces you to focus on the gameplay as the graphical capabilities don't really exist, and there's no sound chip or anything fancy. So, I think I have developed a fun but simple game (with Bounce) in no time at all really, and one that would work well on a portable device, such as a smart phone or something similar, as simple games work well to relieve the boredom of travelling. As the source is in C, this makes all of the logic very portable too!

Thursday, 5 January 2012

Due to popular demand (well, by my standards), here's the second instalment of my exciting 'Return of the Bedroom Programmer' series that I wrote for Micro Mart magazine in the Summer of 2010. Part one is here: Introduction to Z80 assembly part I so if you've missed it, please read through. Note that this is only intended as a guide for beginners and not intermediate or advanced programmers.

Return of the bedroom programmerPart V: More on Colour

Before we delve into the code, let's quickly recap on where we are up to (see the above link if needed). We've briefly looked at what the Sinclair ZX Spectrum was capable of, and how much RAM is available on the different models. RAM is important because the more of it we have, the bigger our program can be and, in many ways, the more we can do.You should now be proficient at writing to the screen as what we've been doing in our code is defining an area of memory (called 'STRING'), putting some text there and then reading each byte into the accumulator (a) and outputting that to the screen using the Sinclair ROM 'PRINT' routine (by rst $10). If you look back on the examples, you'll notice that at the end of the text was two values, 13 is a new line (or carriage return) and zero was the marker for the end of the text. When you get into more advanced programming, you may want to know that using rst $10 affects the af register, but for now, this is not going to concern us.In the last part of the first instalment, we looked at how the colour attribute worked for each character cell and I set you a challenge to change the colours in the code. To do this, you need to change the values of 'a' (the accumulator) before you store it at 23693 and call 3503, and [for the border] change 'a' before calling 8859. If you wanted 'BRIGHT' red text on white 'PAPER' without 'FLASH', the equation would be (0x128)+(1x64)+(7x8)+2, or 122. If you wanted to set the 'FLASH', then you would add 128 to this, totalling 250. Deducting 64 will turn off 'BRIGHT'. As for the border, it can only be one of eight colours, so simply 'load a' with the corresponding colour from zero to seven before you call 8859.Of course, because we clear the screen by calling 3503, anything you set will then be the default value for the text area, therefore if you have set the 'FLASH' value then everything you see will flash.Writing directly to the colour RAM is a better way of doing things, especially if you're having different areas of the screen using the various possibilities available. This area of memory is mapped at 22528 to 23296, with each byte holding the colour information to the corresponding 8x8 character cell. The colour RAM is therefore 768 bytes in total.Let's say we want the first row to flash and the second row not to, here's a quick example:

COLRAM equ 22528 org $8000 ; We'll start our program at 8x4096, or 32768 ld de,COLRAM ; Let's point the register de at the start of the colour RAM ld a,129 ; Flash on, blue text on a black background ld h,32 ; We'll use the register h for a counterLINE1 ld (TEMP),a ; Temporarily store a ld (de),a ; Store a at location bc inc de ; Increase de by one dec h ; Decrease h by one ld a,h ; Transfer h to a cp 0 ; Compare a to zero jr z,NEXTROW ; Go to NEXTROW when a is zero ld a,(TEMP) ; Get a back from TEMP jr LINE1 ; Jump to LINE1NEXTROW ld a,80 ; Flash off, Bright on, red paper, black text ld h,32 ; h is a counter againLINE2 ld (TEMP),a ; Temporarily store a ld (de),a ; Store a at location de inc de ; Increase de by one dec h ; Decrease h by one ld a,h ; Transfer h to a cp 0 ; Compare a to zero jr z,BACK ; Jump to BACK when a is zero ld a,(TEMP) ; Get a from TEMP jr LINE2 ; Jump to LINE2BACK ret ; Return to BASICTEMP defb %11111111; Temporary storage area for a

Two things to note: firstly, the program will be stored at 32768, so it will not work on a 16K machine unless you relocate it. Secondly, it's a rather linear and inefficient way of doing things. As you progress, you'll want to be as efficient as possible, which will free up more memory for you. Think about this for a moment, as this is your next challenge: you could set some parameters (de and h) and then call this as a sub-routine from your main code. This will free up some memory for you and make your source code tidier at the same time.

Part VI: The screen

If you've been paying attention to these tutorials then you'll have started to develop a fairly good understanding of how the screen works, especially the colour attribute cells and where each is mapped in memory and how that corresponds the the character cell. Think of the screen as having two layers, the pixels and the colour attributes, if that helps.If you're following this as I intended, I set you a challenge in part five to alter the example so that you could 'call' a subroutine from the main routine, and gave you a hint as to how this could happen. I think the answer is archived at tinyurl.com/Speccy-Coding, but feel free to ask here if you're struggling.You now know how to colour in each character cell on the old Spectrum (which comprises of 8 x 8 pixels), but we've only touched on writing to the screen using the handy ROM routine. As you may recall, the screen RAM starts at 16,384, and each byte represents eight pixels in a row. The screen's resolution is 256 pixels across by 192 down (referred to as 'scan-lines'). You therefore have 32 bytes per scan-line, 192 times, meaning that the screen takes up a massive 6,144 bytes in total, or 6K out of the 48 available. The screen RAM ends at location 22527, with the colour attributes taking another 768 bytes after that.What I found interesting about the Spectrum is how the screen RAM (excluding colour attribute) is mapped. If you haven't seen how the loading screen is drawn, or at least not seen one recently, then do so as that is exactly how the memory relates to the pixels on the screen. To demonstrate this more clearly, type in the following BASIC (not assembly) program to your Speccy or emulator and RUN it:

Hold down any key and the screen should draw the bit pattern defined after POKE a in line 2 to the screen. You can try changing this bit pattern and see what happens. If you want to find out where exactly you are in RAM then press n at any time. The screen isn't mapped in a linear or logical manner, is it? Or is it?As you will notice, the screen is drawn in three zones, starting at 16,384 to 18,431 (zone zero), representing the first 32 x 8 character cells (rows zero to seven), or 256 pixels by 64 scan-lines, and so 18,432 to 20,479 is the next 32 x 8 cells, and 20,480 to 22,527 is the final zone. In the Spectrum's memory, a binary representation of this is: 010B BSSS LLLC CCCC, worked out as follows:BB = Block (zones 0-2), being either the top third, middle third or bottom third of the screen.SSS = Scan-line (0-7), which relates to the vertical row in the character cell.LLL = Vertical character line (0-7) within the block.CCCCC = Horizontal character co-ordinates (0-31).There is a short-hand way of working this out, as follows:PRINT 16384+(x0*32)+x1+(y+(z*7)*256)What this means is that if you're in zone zero (the first third of the screen), then x0 must be between zero and seven and z must also be zero, and the range for the scan-line (y) in each character cell is zero to seven, with x1 representing the column from zero to 31. This might be a lot to take in, especially if you're rubbish at maths like me.In BASIC, we're only able to 'PRINT AT' to row 21 in BASIC, so with this in mind, here is another demonstrate:

I remember being totally confused by the memory lay out of the screen when I first wrote these tutorials, so I hope that these BASIC examples have helped. Thankfully, our saviour comes in the form of the Sinclair ROM as this will handle everything for us. Give your brain a rest for a while and make a refreshing cup of tea* before continuing.*Coffee and other refreshments also available

Part VII: Pushing and popping

I bet you're really getting to grips with programming the Sinclair ZX Spectrum, aren't you? That game you've always wanted to create, boasting 100% machine code on the cassette label can't be far away now, and I hope you're having fun along the way.One thing you will have noticed is that most of the routines that have appeared so far have used the accumulator (a) and a register pair (bc or de, for instance). You've then typically stored bytes into the accumulator, to set a colour attribute cell directly in RAM, or to write characters to the screen. As we use the registers and the accumulator so often (in fact, we couldn't do much without them), you may occasionally need to temporarily store value, write a new value to do something else, and then restore the old one again. There are two instructions called 'push' and 'pop' which will do this for you, but only work with register pairs. Here's a quick example with the bc register:

What it's doing is storing the byte at DATA1 into the accumulator, pushing bc to the stack before pointing it at DATA2, writing the accumulator there and then restoring the old value to bc before exiting to BASIC. The assembler will make the two bytes at DATA1 and DATA2 read zero and 255 respectively. After executing the routine, they will both be zero. You can see for yourself by PEEKing at the memory locations from BASIC or using a disassembler or monitor from your emulator.If we needed to temporarily store the accumulator (or any unpaired register) we can't use push, but we can declare a single byte of memory in our program as TEMP and put a null byte there; ld (TEMPA),a will save the value, which is then restored by ld a,(TEMPA). Here's an example of this in action (you may want to set your own colours and clear the screen first):

org $8000 ; Store our program at 8*4096INFINITE ld a,22 ; 22 is the Sinclair ROM value for PRINT AT rst $10 ; Call the ROM PRINT routine ld a,0 ; We need an x and y co-ord for PRINT AT, so a=0 rst $10 rst $10 ; We call it twice (PRINT AT 0,0;) ld bc,STRING ; Points the register bc to STRINGLOOP ld a,(bc) ; Puts the current byte at bc into a cp 0 ; Compares a to zero jr z,MOVEBYTE ; If equal to zero, jump to MOVEBYTE rst $10 ; Outputs a to the screen inc bc ; Increases bc by one jr LOOP ; Goes back to LOOPMOVEBYTE ld bc,STRING ; Let's point bc at the start of STRING again ld de,TEMP ; And de at TEMP, one byte before STRING ld a,(bc) ; Get the first byte at bc and put into a ld (de),a ; Store a at TEMPSCROLL inc bc ; Increase bc by one inc de ; Increase de by one ld a,(bc) ; Get the current byte at bc, store in a cp 0 ; Compare to zero jr z,AGAIN ; We'll jump to AGAIN if a=0 ld (de),a ; Store a at de (one byte before bc) jr SCROLL ; Jumps to SCROLLAGAIN ld a,(TEMP) ; a=TEMP ld (de),a ; Store a at location de (end of STRING before zero) ld b,8 ; Now we're using the b registerDELAY halt ; Stop everything temporarily djnz DELAY ; Counts down b register until zero jr INFINITE ; Jump back to INFINITETEMP defb %11111111; byte used in MOVEBYTE and AGAINSTRING defb " Micro Mart Rulez! " ; 32 characters in total defb 0 ; End of string

I'll let you find out what this routine does (a hint is in the labels though). The challenge for this instalment was at tinyurl.com/Speccy-Coding, though you'll have to plough through and find it.

Part VIII: Changing channels

Those of you who tried last week's main routine will have noticed two things: firstly, it was moving the bytes about to do what was once called a 'scrolly text' (now referred to as an 'animated marquee'), and secondly it introduced a rather handy delay routine. Even though the Z80 inside the Sinclair ZX Spectrum is 'only' clocked at around 3.5Mhz (by today's standards, this is nothing), you may still need to slow things down a little sometimes. Additionally, if you ran the routine with PRINT USR 32768, it will have scrolled on the top line of the screen, whereas if you used RANDOMIZE USR 32768 then it would have appeared on the next to last row unless you set your own default colours and cleared the screen first.As I have said previously, the Spectrum's screen is mapped logically in memory in three zones, but the Sinclar ROM sees it as two channels. The lower two rows of text are usually reserved for BASIC entries, and the 'user' screen is therefore everything above that. Of course, you can write anywhere to the screen by storing the relevant bytes you like, bypassing the ROM entirely, but this requires some maths as demonstrated above.Thankfully, the boffins at Sinclair were kind enough to allow us to select what part of the screen we're writing to, and here's a quick example:

ORG $8000 ; Put the program into 8*4096 (32768) ld a,2 ; 2 opens the channel for the upper screen call MAIN ; Let's call the main sub-routine ld a,1 ; Okay, now we want to write to the BASIC area call MAIN ; Let's write some text again ld hl,23560 ; This is part of the keyboard buffer ld (hl),0 ; We'll clear itLOOP ld a,(hl) ; Put the current value of hl into a cp 0 ; Compare it to zero jr z,LOOP ; If equal to zero, go back to loop ret ; Key pressed, so return to BASICMAIN call 5633 ; Open channel set by accumulator ld de,STRING ; Point de to the start of the STRING data ld bc,EOS-STRING ; Tells the program how long the STRING data is call 8252 ; Print the STRING out to the selected screen channel ret ; Return from sub-routineSTRING ; Here's the string data defb "http://www.micromart.co.uk/"EOS defb 0

Okay, so that's a bit different to how we've been doing things, and you may notice that we've got a fairly rudimentary keyboard reader in there too, like using PAUSE 4e4 in BASIC when you tell the user to "Press the any key" or something similar.What's happening by calling 5633 is telling the ROM where to write the text to; channel two is for the upper area of the screen, channel one is for the lowest two lines of text, and there's also a channel three for the Sinclair printer too, should you ever need to use it.The register pairs de and bc are then telling the routine at 8252 where in memory the data is to 'write' to the currently open channel, (de is the start of memory, and bc is the end). You'll notice that the text is written to the very bottom line for channel one, so have a think about pushing it up one line.This technique is very handy as it uses a small amount of code. It's time for a break again. Keep on coding over the next few weeks until I sort out the final exciting* instalment.*It might not be that exciting.

Monday, 2 January 2012

What starts with C and works on the Sinclair ZX Spectrum? Well, thanks to z88dk - available from www.z88dk.org - it's C.

I've been playing around with this and, I have to say, the Wiki is useful but not very helpful, and I can't find many clear examples that'd be good for beginners to get their teeth into. This, in my opinion, is due to lack of good commenting in the source code examples, and also it being written by many technically minded people who forget that sometimes things need to be explained in English, which is a language that I'm quite fond of as it can be very clear and concise if used well. So, I intend to write a beginners guide to C programming for the ZX Spectrum in the near future.

Anyway, after scratching my head for a few hours, I wrote a simple 'Hello World!' type program. This isn't a challenge, of course, but I wanted to change the colours of the text and bypass the 64-column mode which is what the z88dk compiler defaults to when you use it. I also got it to drop down to assembly for a short routine which sets up the default colours for the whole screen. Anyway, more importantly, I've commented it quite well so even if you're a complete novice at programming and would struggle with something simple, it should be straight forward enough. Note that there is a much simpler way to do the same thing, but simple doesn't always tell you everything that you need to know. Oh, and it also uses a look-up table, something that I always find myself building even if it's not always strictly necessary.

Here's the code:

/** This does something similar to the classic * "Hello World!" code that's a popular starting * point for learning programming. */#include < stdio.h > // Remove the spaces here as Blogger thinks it's an HTML tag// Here are our default colour attributes#define INK 7#define PAP 2#define FLA 0#define BRI 1#define INV 0#define BOR 5// This is used in the setup function, which drops into// machine code - see my blog for more information:#define COL 128*FLA+64*BRI+8*PAP+INK// Forward declarations of functions:static void main(void);static void setup(void);static void hello(void);static void magazine(void);// Global variables:static int i=0;// Here is an array which will set up the default colour// attributes for printing our message to the screen,// 255 is used as a 'terminator' to say "we've finished here"static int screensetup[]={ 1,32,16,48+INK,17,48+PAP,18,48+FLA,19,BRI,20,48+INV,12,255};// This is the 'entry point' of our program:void main(void){ // This will call a routine to set the default // colour arrtibutes for the whole screen as // defined above: setup(); // This does the same for outputting out character // see http://www.z88dk.org/wiki/doku.php?id=platform:zx // for more information under the heading "The standard // ZX Spectrum console driver" - hopefully, these numbers // will now make more sense! while(screensetup[i]!=255) { // The %c means 'print character code' or something similar printf("%c",screensetup[i]); // Increase i to read the next element of the array: i=i+1; } // Calls our functions, firstly hello: hello(); // and now magazine: magazine();}// This function sets up the default colours for our screen as// defined above:void setup(void){ #asm // Sets default ink and paper colour, then clears screen ld a,COL ld (23693),a call 3503 // Sets border colour ld a,BOR call 8859 #endasm}void hello(void){ // Here is where the magic happens, can you tell what it does? printf("Hello ");}void magazine(void){ // And what about this bad boy then? printf("Magazine!\n");}

Call the source code "HelloMagazine.c" (obviously without the quotation marks) and place it in the same directory as the z88dk 'bin' folder, go to your command line prompt and compile it with:

zcc +zx -lndos -create-app -o hello HelloMagazine.c

it should create a file called "hello.tap", simply load this into your emulator and watch with amazement as the magic actually happens!

PS, I'm certainly not endorsing any printed-matter publication which has all of the latest celeb goss or whatever - it's a joke as I've read too many times about "Hello World!" being the default starting point, so I always try to avoid it.