Z80 Software Delay
Asked Answered
W

2

6

I am trying to create a software delay. Here is a sample program of what I am doing:

    Address  Data   Opcode  Comment    
    1800      06    LD, B   Load register B with fix value
    1801      “ “           Fixed value
    1802      05    DEC, B  Decrement value in register B
    1803      C2    JP cc   Jump to 1802 if value is not 0
    1804      02    -       Address XX
    1805      18    -       Address XX

My question is how can I calculate the required fixed value to load into register B so that the process of decrementing the value until 0 takes 2 seconds?

In my manual the time given to run the instructions is based on a 4MHz CPU but the Z80 CPU I am using has a speed of 1.8MHz. Any idea how I can calculate this? Thanks. P.S here is the decrement (DEC) and jump (JP cc) instructions from the manual:

Instruction    M Cycles    T states   4 MHz E.t
DEC r             1           4         1.00
JP cc             3       10 (4,3,3)    2.50
Wrapped answered 21/3, 2015 at 16:12 Comment(0)
M
4

If by 1.8MHz you mean exactly 1,800,000 Hz, then to get a 2 second delay you'd need to delay for 3,600,000 T-states. Your current delay loop takes 14 T-states per iteration, which means that your initial value for B would have to be 3600000/14 == 257143, which obviously won't fit in one byte.

The greatest number of iterations that you could specify with an 8-bit register is 256, and to reach 3,600,000 T-states with 256 iterations, each iteration would have to take 14,062 T-states. That's one big loop body.

If we use a 16-bit counter things start getting a bit more manageable. At 65,536 iterations we only need 55 T-states per iteration to reach a total of 3,600,000 T-states. Below is an example of what that could look like:

    ; Clobbers A, B and C
    ld      bc,#0
1$:
    bit     #0,a    ; 8
    bit     #0,a    ; 8
    bit     #0,a    ; 8
    and     a,#255  ; 7
    dec     bc      ; 6
    ld      a,c     ; 4
    or      a,b     ; 4
    jp      nz,1$   ; 10, total = 55 states/iteration
    ; 65536 iterations * 55 states = 3604480 states = 2.00248 seconds
Monarchal answered 21/3, 2015 at 17:28 Comment(3)
Works well, and well explained, thank you. However, or a,b is not a Z80 instruction, it should be or b.Godart
@Olivier'Ölbaum'Scherler That depends on the assembler used. My example was written in sdasz80 syntax, where the explicit form or a,b is allowed.Monarchal
Well, if your assembler supports it, then I’m happy for you, but the fact is that this form does not exist either in the Z80 CPU User Manual from Zilog, nor on z80.info or Z80 Heaven. So why not use the regular form and spare future visitors a headache?Godart
I
2

I'm a bit of an optimization freak, so here is my go using the syntax with which I am most familiar (from the TASM assembler and similar):

Instruction   opcode    timing
ld bc,$EE9D   ;01EE9D   10cc
ex (sp),hl    ;E3       19*(256C+B)
ex (sp),hl    ;E3       19*(256C+B)
ex (sp),hl    ;E3       19*(256C+B)
ex (sp),hl    ;E3       19*(256C+B)
djnz $-4      ;10FA     13cc*(256C+B) - 5*C
dec c         ;0D       4*C
jr nz,$-7     ;20F7     12*C-5

This code is 12 bytes and 3600002 clock cycles.

EDIT: It seems like part of my answer is gone! To answer your question better, your Z80 can process 1800000 clock cycles in one second, so you need twice that (3600000). If you add up the timings given in my code, you get:

=10+(256C+B)(19*4+13)-5C+4C+12C-5

=5+(256C+B)89+11C

=5+22795C+89B

So the code timing is largely dependent on C. 3600000/22795 is about 157, so we initialize C with 157 (0x9D). Plugging this back in, we get B to be roughly 237.9775, so we round that up to 238 (0xEE). Plugging these in gets our final timing of 3600002cc or roughly 2.000001 seconds. This assumes that the processor is running at exactly 1.8MHz which is very unlikely.

As well, if you can use interrupts, figure out roughly how many times it fires per second and use a loop like halt \ djnz $-1 . This saves a lot more in terms of power consumption.

Invasive answered 22/3, 2015 at 4:16 Comment(1)
"This saves a lot more in terms of power consumption". Not sure if this statement is actually true as HALT repeats NOP operations until a maskable interrupt has occurred. I didn't think different op-codes caused any significantly more or less power consumption than others. I may be wrong though.Jetliner

© 2022 - 2024 — McMap. All rights reserved.