Monday, 13 April 2015

Old and Grumpy...

I think i must be getting old and grumpy, it seems like every year there is a new addition to languages that you need to learn.  Im not sure these are all good things myself.  Im mainly moaning about the new (and not so new) features to C++ like foreach and auto.

Lets clear one thing up, i dont find it hard to understand these, ive used similar in many languages but im not sure they belong in C++.  I feel that all it really does is make for lazy programming.  Why do you need a foreach instruction, it can be done just as easily in a standard for instruction.  What is the advantage beyond having to type a few less characters.  I can tell you one thing.. It damn well slows down the compiler.

I remember getting the first Intel P90 in the office to compile on, it was amazing, code built in no time.  Now i find myself waiting well over an hour sometimes for something to compile! Im on a i7 Six Core with 32Gb of Ram running SSD, this should never NEVER happen!

So yes, im sure lots of people will tell me how wonderful it is to use foreach/auto etc and all these things, im sure they will give good reasons for them... to me you are wrong.. its lazy, just learn to type faster, and then maybe we can all compile code quicker again....

Updates to interrupt details

Following some questions about the interrupt details posted, i updated the end of the document to include the following paragraphs.

RET / RETI

You may have noticed that the interrupt routine ends with RETI rather than the standard RET, this is for completeness and not specifically required, you can end with RET if you prefer.  The main reason RETI was added to the instruction set is to allow hardware to detect when the end of the interrupt routine and to pass control onto the next interrupt in a dasiy chained system.  The hardware could specifically check the opcode for RETI.  If it checked RET it could get confused when coming to the end of a function rather than the entire interrupt routine.  To my knowledge no devices for the spectrum use this feature.

EI but not DI?

There is another oddity in the interrupt routine, we have an EI at the end but we never do a DI.  This is due to the fact the Z80 will do an internal DI when it starts processing the interrupts.  The reason for this is to stop multiple interrupts triggering on top of each other and overrunning the system stack.  You can do an EI before the end of your interrupt routine if you wish but general practice is to wait until you have finished then enable them, that way there is no chance of you starting a new interrupt routine before the old one has finished.

Interrupts on the ZX Spectrum

What are interrupts?

Interrupts, as the name suggests, are when the normal running of a computer program are interrupted so something else can happen.  On modern computers there can be various interrupts, but on the ZX Spectrum there were very few, most times you will hear of only 3 different interrupt modes 0, 1 and 2.  While the main focus of this document is interrupt mode 2, we will quickly cover modes 0 and 1.
These three interrupts are all maskable, that is to say we can stop the effect of them by using the DI (Disable interrupts) instruction.  If we want them to start happening again we use the EI (Enable interrupts) instruction.  For completeness I will point out that when we use the DI instruction, the interrupts still trigger, that is the Z80 is still aware of them, it just ignores them and carries on doing what it was doing anyway. 

 

Interrupt Mode 0

This interrupt mode is not really used.  The reason for this is that it is triggered by external hardware. The devices must place instructions (such as RST or CALL) onto the data bus when it sets the interrupt request on the Z80.  It is this interrupt mode however that is running when the spectrum first powered up, luckily the Spectrum ROM soon setups up Interrupt Mode 1.

Interrupt Mode 1

This interrupt is very similar to IM2 (Interrupt Mode 2) in that it is triggered by the vertical blanking of the screen refresh that happens roughly 50 times a second.  The vertical blank is the time when the beam that draws the screen has reached the bottom and is turned off while it moves back to the top, ready to draw the next frame.  Mode 1 is the standard interrupt used by the Spectrum while running basic and, unless you alter it, while your programs are running. 

When the vertical blank occurs the current contents of the program counter (PC) are pushed onto the stack (SP).  The address $0038 is then loaded into PC and the Spectrum will start running the program stored at this location.  As you may notice by the address, this is in the ROM and as such we have no way of altering what this does.  The ROM routine mainly scans the keyboard and stores details of what is being pressed.

Interrupt Mode 2

As mentioned IM2 is very similar to IM1 above except it is known as a vectored interrupt.  Like IM1 it is triggered roughly 50 times a second on the vertical blank, it pushes the current PC onto the stack but then rather than jumping to a specific address it uses a vector table to find out what value to load into PC.  There is a lot of confusion over the exact operation of this vector table, and as different emulators deal with it in different ways, it is best to go with the method that works for all. 

The vector table is simple a list of memory addresses, 128 of them in total.  The table sits on a 256 byte boundary and the high byte of its address is loaded into the special I register on the Z80.  The confusion seems to occur with what fills in the bottom 8 bits.  The value comes from the BUS and general feeling is (and as such the safest rule to follow) that any value can occur.  Some documentation such as ZAKS (Programming the Z80 book) will say that only the higher 7 bits will be used from the BUS, the least significant bit will always be 0.  While to me this seems the most logical given the vector table, as mentioned not all emulators follow this rule and as such I would strongly advise against assuming this bottom bit is 0.  Why does it make a difference you may ask? To answer that lets look at what happens when the interrupt is triggered.    

When the interrupt is triggered, and they are enabled, the contents of the PC are pushed onto the stack. The contents of the I register are loaded into the upper 8 bits of the address bus, the BUS supplies the lower 8 bits, as we are not using any devices these bits can be any value.  .  Given this address ((I * 256) + BUS bits) a two byte address is read and loaded into the PC. The Z80 will then run the code stored at this address as per normal. 
First lets assume that the I register is set to $40 and that the bottom bit is always 0.  When the interrupt is triggered the memory address will we calculated as $40xx where xx is any even 8 but value (0, 2, 4… 254).  We can simply store the address of our interrupt routine at all 128 locations $4000, $4002, $4004 …… $40fe.  This way when the interrupt occurs the same routine address will be loaded into the program counter no matter what the BUS value is.  The code to set this up would be something like

              ORG           $8000

; Setup the 128 entry vector table
di

              ld            hl, VectorTable
              ld            de, IM2Routine
              ld            b, 128

              ; Setup the I register (the high byte of the table)
              ld            a, h
              ld            i, a

              ; Loop to set all 128 entries in the table
_Setup:
              ld            (hl), e
              inc           hl
              ld            (hl), d
              inc           hl
              djnz          _Setup

              ; Setup IM2 mode
              im            2
              ei

              ret

; Basically nothing
IM2Routine:  
              ei
              reti

; Make sure this is on a 256 byte boundary
              ORG           $F000
VectorTable:
              defs          256

Hopefully the code shows that the vector table stores the 128 entries pointing to the routine IM2Routine.  When the interrupt is triggered the byte address at (I * 256) + Bus will be read along with the following byte to form the address.  The routine at this address will be called, in our example no matter what the Bus is it will always call IM2Routine.  But what happens if we don’t assume bit 0 will always be 0?
In our example above IM2Routine will have the address $8016, so the first eight bytes of our vector table will be

defb          $16, $80, $16, $80, $16, $80, $16, $80


The rest of the table will follow the same pattern.  When bit 0 was always 0 the low byte would always be pulled out as $16 and the following high byte of the address would be $80 (assuming Bus was 0 the low byte would be pulled out from the start of the table and the high byte would be the next byte), the next option would be Bus was 2 which would give the same result just reading from 2 bytes into the table.  If however bit 0 was not always 0 we could end up reading starting from byte 1 in the table giving our low byte of $80 and our high byte of $16.  This gives us the address $1680 which certainly isn’t where we want the Z80 to jump to.

Given the reasons behind how the vectored interrupt is designed I would speculate that the hardware does indeed always reset bit 0 to 0 however as mentioned not all emulators follow this rule (The hardware ive tested this on does mind you).  To be safe its best to work on the theory it can do a read from any memory address.  So, how do you deal with that?  Simply make sure all the bytes in the table are the same value.

One other thing to note with the issue of reading from any byte is what happens if the Bus value is 255 when the interrupt triggers.  This will be used as the low end byte of the address on the vector table, but as we need to read two bytes it will read the last entry in the 256 byte table as well as the byte following it.  To allow for this we must use a 257 byte table when allowing for any Bus value.  So with that in mind lets take a look at the code to setup the table.

              ORG           $8000

; Setup the 128 entry vector table
di

              ld            hl, IM2Table
              ld            de, IM2Table+1
              ld            bc, 256

              ; Setup the I register (the high byte of the table)
              ld            a, h
              ld            i, a

              ; Set the first entries in the table to $FC
              ld            a, $FC
              ld            (hl), a

              ; Copy to all the remaining 256 bytes
              ldir

              ; Setup IM2 mode
              im            2
              ei

              ret


; This routine now needs to be at a specific address (remember we only have from $FCFC to $FE00 else we will overwrite our table)
              ORG           $FCFC
; Basically nothing
IM2Routine:  
              ei
              reti

; Make sure this is on a 256 byte boundary
              ORG           $FE00
IM2Table:
              defs          257

Why do we need an interrupt routine?

Having looked at how we setup an interrupt routine, you may find yourself asking why you need one.  Well there are a few good reasons to set one up, firstly why let the ROM waste good cpu cycles doing something that you may not need?  The main reason however often comes down to timing, especially with things like music.
A music player will usually require that you call an update function every frame, which is 50 times a second.  If you don’t do this then the music will start to sound very strange indeed as frequencies etc will all go wrong.  While it might seem easy to do this if you know your program will always run in a frame, when you start to write more complex programs and games this might not always be the case.  In a game you may have an unknown number of enemies on the screen firing an unknown number of bullets.  While your code might happily run in a frame if there are 5 enemies, what happens when the 6th one appears?

To solve this we can put the music update routine into the interrupt, then no matter how long the game takes to update the music will continue to be updated at a constant 50 frames a second.  To do this will simply add a call to the music player update in the IM2Routine. 

; Update a music player
IM2Routine:  
              call          Music_Update
              ei
              reti

Seems easy enough right? Well yes and no, the chances are this would crash your computer horribly.  The reason is that the interrupt can occur at absolutely any time and it doesn’t care what the state of your registers are.  When we just had EI/RETI it was fine, unfortunately the music update function will most likely alter registers, and even if it handles storing and restoring them all itself, if it very good practice to store and restore them yourself to be sure. 

; Update a music player with register storing / restore
IM2Routine:  
              push          af
              push          hl
              push          bc
              push          de
              push          ix
              push          iy
              exx
              ex            af, af’
              push          af
              push          hl
              push          bc
              push          de

              call          Music_Update

              pop           de
              pop           bc
              pop           hl
              pop           af
              ex            af, af’
              exx
              pop           iy
              pop           ix
              pop           de
              pop           bc
              pop           hl
              pop           af
              ei
              reti

Just to clear up a few things…

The Z80 provides 2 other interrupts that are not maskable, that is you cannot disable them with the DI instruction, they are the NMI and BUSRQ.  To my knowledge the BUSRQ was never used as this was designed to allow DMA systems to work along side the Z80, something the spectrum never had.  The NMI (Non-Maskable Interrupt) was used on the Spectrum but was only available via additional hardware. 

Certain hardware devices had a button that would trigger a NMI which would cause the current program to stop and the routine at address $0066 to be executed.  As this address is fixed and in the ROM it is of very little use for software programmers.


RET / RETI

You may have noticed that the interrupt routine ends with RETI rather than the standard RET, this is for completeness and not specifically required, you can end with RET if you prefer.  The main reason RETI was added to the instruction set is to allow hardware to detect when the end of the interrupt routine and to pass control onto the next interrupt in a dasiy chained system.  The hardware could specifically check the opcode for RETI.  If it checked RET it could get confused when coming to the end of a function rather than the entire interrupt routine.  To my knowledge no devices for the spectrum use this feature.


EI but not DI?


There is another oddity in the interrupt routine, we have an EI at the end but we never do a DI.  This is due to the fact the Z80 will do an internal DI when it starts processing the interrupts.  The reason for this is to stop multiple interrupts triggering on top of each other and overrunning the system stack.  You can do an EI before the end of your interrupt routine if you wish but general practice is to wait until you have finished then enable them, that way there is no chance of you starting a new interrupt routine before the old one has finished.