A whole lot of question (z80 related, along with some others)

Status
Not open for further replies.

Marks256

New Member
I keep thinking of a very large amount of question that i need answered, and then i end up forgetting them, so i want to ask a few here before i forget. Then i will keep adding to this thread as my questions come...


1) How does address decoding work?

I understand how addressing works now (thanks to kchriste, and other members in my "multiplexing" thread from a long time ago) Now my question is, how do i use the top address lines to map memory/other hardware (efficiently) I know that i just have to use simple logic chips (AND, OR, NOR, NAND, etc), but i dont' understand how people can get by with just a few of them?

Take this one for example
http://www.z80.info/gfx/z80test.gif

How do you know that the PIO is 00h to 03h? If my logic (as in mental problem solving logic), shouldn't the PIO in the schematic above be 00h to 7Fh? and then the UART should actually be 80h to FFFFh, because the logic enables the uart at and after 80h.

How would one "limit" the range better? What if i wanted to access a single device at 90h, and another device at 91h, and yet another at 92h? How would one do that?



2) Interrupts?

From what i already know, an interrupt is a way for events to be raised to the processor when needed, so the processor doesn't have to poll all the time. My question is, how is this done on the z80? I am assuming it is through software, so, how does it work?

I have also heard of Interrupt Controllers. Is that what needs to be use? What does a controller consist of? How does IT work?






More later
 
Interrupts, as far as I have seen and know all work in the same basic way:
When a particular interrupt occurs the processor jumps to a reserved area in memory (which contains a table of addresses). The specific "entry (location in memory) that the processor jumps to depends on the interrupt that was triggered. The address contained at this location in memory points to the subroutine that is to be run for the interrupt, and the processor jumps to that address and runs the subroutine. THe interrupt subroutine should store the system's previous state when it starts running and restore the state after it finishes.

So it's basically writing your interrupt subroutine, and writing the address that this subroutine is stored at into the specific location in the "interrupt vector table" (that reserved location of memory which is basically a table of stored addresses) that is associated with your interrupt.

An interrupt controller is just the circuitry inside the uC that handles all the interrupt stuff, isn't it?
 
Last edited:
To get a handle on address decoding you should look at an actual decoder chip like a 74LS138 or 74LS139 or their CMOS equivalents. On the 74LS138 you will notice that there are three inputs labeled A, B, and C. These inputs would be connected to address lines A13, A14, and A15 respectively. You will also notice three enable inputs. One is active High and two are active low these might be tied to Vcc anf GND respectively.

What will the outputs do? Well Y0 = 0 whenever A15 = A14 = A13 = 0 and so on. This circuit will break up the 64K memory space into eight 8 K blocks. There is no right answer to how people divy up the 64K space it depends on lots of things.

Interrupts are accomplished by forcing a special instruction onto the data bus during the M1 cycle (instruction fetch) These instructions are call RESTART instructions and there are eight of them. they are like one byte subroutine calls to fixed locations
Code:
RST 0 --> 0x0000
RST 1 --> 0x0008
...
RST 7 --> 0x0038
RST 0 is also equivalent to a power up reset.

One of the Z80 predecessors (8085) had three additional RST instructions called RST 5.5, RST6.5, and RST 7.5 associated with dedicated lines on the processor. I dont remeber where they vectored to. The Z80 may also have had additional interrupt mechanisms which I've forgotten.

Check out pages 24-25 of the following
https://www.electro-tech-online.com/custompdfs/2008/01/z80cpu_um.pdf
 
Last edited:
I found a good page on Z80 **broken link removed**.

The Z80 has 3 interrupts modes. The simplest is mode 1 where all interrupts go to rst38 (jump to location 0x38). The other modes require hardware to place values on the bus during an acknowledge cycle and so get complex. There is also a Non Maskable Interrupt (NMI) which cannot be turned off and jumps to location 0x0008.

As for address decoding, the Z80 handles I/O seperate to memory and so you have the full address space to play with. In your example, the PIO will be mapped at 0,4,8 .. ,7c but it doesn't matter, the space may as well be used.

Mike.
 

Ok. Thanks. So how would you define the subroutines? Where ever the interrupt make the cpu look in memory?





Aaaah. I see! So if i had a device that wanted to request an interrupt, i would wait for the CPU to attempt to fetch the next instruction, but inject the interrupt command instead? Then the CPU will do that subroutine, and then continue back where it was?

What if the CPU was doing something highly important (like adding two registers). What if the interrupt causes the numbers in the registers to change? Or do i have to write code to "cache" the valuable data into memory, and put it BACK into the registers after the sub?


As for the demux's, i already understand those (well enough to use them, anyway). But like my example in the OP, what if i just want a single address? like what if i had a simple input device that simply gives the CPU a number? How would i define a SINGLE address, instead of a range of addresses?






That brings me to another question. What is a "Stack". I do know that it is like a "stack" of memory, but a better explanation would be nice...

Thanks for the help so far.
 

Yep. I knew that the z80 handles io and memory separately. Thanks for the website. I came across it before, but i have it another look.
 
Last edited:
If you want to decode a single address then you need a circuit with 16 inputs. Use OR gates for the zeros, use NAND gates for the ones, then OR the result. It will be low when the address bus matches the combination of 1s and 0s you've chosen. The way I like to think of gates is to say either the explict function or its DeMorgan equivalent in words:

OR Gate:
A HIGH OR a HIGH is a HIGH ≡ A LOW AND a LOW is a LOW

NAND Gate:
A HIGH AND a HIGH is LOW ≡ A LOW OR a LOW is a HIGH

Try it -- you'll like it

Interrupts can only happen between instructions, and they must save and restore anything that they will alter. It is also worth noting that it would be a very bad practice to modify a memory variable in both an interrupt and a non interrupt routine. It is also an execrable practice to call the same function(subroutine) from an interrupt and a non-interrupt context. You would be surprised how many times I've come across these things cleaning up OPMs.

I think NMI goes to 0x0066

Aside from the ability to push the registers onto the stack there is an alternate set of registers for just this purpose. They are called the primed set. Check out pages 123 and 124 in the reference I gave you for the following instructions:
Code:
    EX  AF,AF'      ; Exchange A and PSW with primed set
    EXX             ; Exchange BC, DE, HL with primed set
 
Last edited:
kchriste said:
A short explanation of the z80 stack:
https://www.smspower.org/dev/docs/wiki/Z80/Stack
Also Wiki has a simple explanation on this page entitled "Basic architecture of a stack":
**broken link removed** _stack

Wow. That is a first! Something "basic" on wikipedia!

Thanks, kchriste. That explained quite a bit to me.

Papabravo said:
If you want to decode a single address then you need a circuit with 16 inputs

Aaah. I thought as much. Would i have to use the same basic principle to define a range of addresses?

Papabravo said:
and they must save and restore anything that they will alter.

Ok. So, say for example i have a very simple input device. let's say a simple keyboard. When a key is pressed, an interrupt is raised. So the processor catches it, and jumps into a sub routine. For the sake of example (i am just making this up, as i do not know enough assembly to quite know what i am talking about...), that the subroutine loads the value of key pressed into register "A". What would have to be done in order to tell the program what key was pressed? Would i load it into memory at a specific address, and have the program poll that address for changes? That would defeat the purpose of an interrupt, though... Also, how would the sub restore the previous value of "A"? Put it into stack?


Thanks
 
Would i load it into memory at a specific address, and have the program poll that address for changes?
Yes, that is the jist of it. You would use the interrupt routine to empty the keyboard buffer and place the "key presses" in global memory so that the buffer is empty to receive the next key press. Then your main routine can take its own sweet time to deal with the key presses in global memory when it is ready to do so.
 
Well interrupts by definition cannot return values. The more interesting thing to do is to place the "keys" in a FIFO queue. That is a fancy name for a data structure where the first key put into the queue (by the interrupt service routine) is the first key taken out of the queue (by the background program). This way the interrupt is serviced in a timely fashion, and the background program can take it's sweet time before it gets around to processing the data.

So what you need for this queue is
  1. A chunk of memory called kb_queue[256]
  2. A put index called kb_put
  3. A get index kb_get
  4. A count of the number of keys in the queue called kb_n
The initial condition is all variables initialized to zero.

Now you need two functions called kb_EnQueue that will put 1 byte of data into the queue, and a function kb_DeQueue that will take one byte of data out of the queue.

In C-like pseudo code here goes

Code:
void  kb_EnQueue(unsigned char c)
{
    if(kb_n > 255) return ; /* Queue is full */
    kb_Queue[kb_put++] = c ;
    ++kb_n ;
}

unsigned char kb_DeQueue(void)
{
    --kb_n ;
    return  kb_Queue[kb_get++] ;
}
Function kb_EnQueue is called inside the interrupt service routine. Note that it modifies variables kb_put and kb_n. Notice also that when either of these variables is equal to 255 and they are incremented then they roll over to 0.

In the background program there would be a sequence like the following
Code:
...
    if(kb_n > 0)    /* If there is data in the Queue  */
    {
      key = 0 ;
      disable_interrupts() ;
      key = kb_DeQueue() ;
      enable_interrupts() ;
    }
...
Interrupts must be disabled for a short time during which you will modify the common variable kb_n.

The interrupt routine service routine never touches kb_get, and the background program never touches kb_put. They both touch kb_n and that is why we need to protect that critical section.

I should point out that there are ways to use smaller queues and there are ways to avoid using kb_n. At least this way you can focus on what is happening instead of tying yourself in knots tying to super code the last scraps of time and space out of the routine.

One last thing to note is that these routines work for all sorts of character oriented peripherals like serial ports, SPI ports, parallel ports, CAN peripherals and so forth. They are a handy thing to keep in your toolbox.
 
Last edited:


none of that made any sense...








Aside from that, i have ANOTHER question. How does a keyboard handle multiple key presses? One or two keys at a time, i can understand, but three or four at a time, i don't understand how that is possible?
 
Haven't you started using a PIC?
You can study interrupts with it.

The device can "request" an interrupt at any time. It might wait for an "acknowledge"; at which time it can finish up, and prepare for the next request. Some devices will automatically "release" the interrupt on an acknowledge; others will need to be told you are "finished", before they release the interrupt.

I would suggest a clock or timer as a first interrupt project. Not a keyboard buffer.
 

Let's not start the "PICs are better" thing. Ok? I will start with PICs when i feel that i am good and ready.


I would suggest a clock or timer as a first interrupt project. Not a keyboard buffer.

I am asking "what if" type questions. I will stick with just trying to do simple things before i do anything radical and fancy.
 
Marks256 said:
Let's not start the "PICs are better" thing. Ok? I will start with PICs when i feel that i am good and ready.

I suggest you try listening to the sound track from "The Sound Of Music"


You would do well to heed that advice, instead of repeatedly asking the same questions over and over again and paying no attention to the answers.
 
If anyone on here Wants a Z80 chip, I have one.

I have no use for it and I will Mail it to that person for FREE.

However, I am just leaving to go on a Holiday to Chile, so DON'T email me till after March 3, 2008.

Take care.....Gary
 
Nigel Goodwin said:
Chile?, that sounds a bit random?
Chile is mainstream. Now Tristan da Cunha, Peter I, or Pitcairn Island those would be outliers.
 
Nigel Goodwin said:
Chile?, that sounds a bit random?

One Week at the Very bottom of Chile, (To See Penguins and there offspring) One week up on the Main land of Chile, (Sightseeing and visit with a guy I met on the Internet) Than off to Hawaii to warm up Before returning home.

Your Just Jealous!
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…