What's wrong with my Z80 CTC code?

483 views
Skip to first unread message

Jacob Hahn

unread,
Aug 30, 2020, 6:42:09 PM8/30/20
to retro-comp
Hi,
I'm trying to test the Z80 CTC on my ZTO-80 SCB, and I've confirmed it to work using a logic probe. However, my software, which prints a message whenever the CTC outputs, does not work. 
The idea of the software is to have the CTC interrupt the CPU, which is halted under normal operation, whenever it hits zero, which then uses a Z80 SIO to print "TIMER DONE". The problem is that the CTC only activates the INT pin once, and even then the SIO does not print. Another side note is that the CTC, which is first in the interrupt daisy chain, appears to be holding IEO low. I don't understand why it is.

Here's my code:

Jay Cotton

unread,
Aug 30, 2020, 9:57:15 PM8/30/20
to retro-comp

Jacob Hahn

unread,
Aug 30, 2020, 10:18:39 PM8/30/20
to retro-comp
Jay,
Thanks for the link. That's the document I used to start my own code. I didn't follow it exactly, of course, but that's what I used.

Jacob Hahn

unread,
Aug 30, 2020, 10:20:25 PM8/30/20
to retro-comp
I'm mostly concerned about if it is a software or hardware issue, because this test is supposed to show if I need to rework my CTC hardware or not. From what I can tell, it's a software issue, but I'm not completely sure.

Mark T

unread,
Aug 30, 2020, 10:35:20 PM8/30/20
to retro-comp
From your description it seems the ctc is not seeing the interrupt acknowledge, otherwise it should release the int output. 

Try modifying your code slightly, remove the halt from the loop, and place a halt at the start of the interrupt  service routine. Then it should be easier to see if the interrupt service routine is ever executed.

Do you have a scope to try and capture IORQ and M1?

Mark

Jacob Hahn

unread,
Aug 30, 2020, 10:57:12 PM8/30/20
to retro-comp
Mark,

I'm sorry, I should clarify. The CTC pulses  INT only once (about one second after reset, so that's expected). Then, it stays high (inactive). Under normal operation, I would expect it to pulse INT once about every second, because that's the frequency generated by the CTC, and it is setup to INT on a zero count condition.

There are two other issues, as I had mentioned:
  1. The INT that is produced does not result in the SIO printing "TIMER DONE".
  2. The CTC holds its IEO output low (I have verified that it is receiving 5V on IEI). I have now found that IEO is HIGH up until the first INT pulse. Then, it transitions to LOW and stays that way. Do I have to reset some bit in the CTC to allow it to release IEO?

Thanks.

Mark T

unread,
Aug 31, 2020, 3:50:18 AM8/31/20
to retro-comp
Maybe there is a problem in the print routine, not printing message and not returning to execute the reti, so then the ctc doesn’t  see a reti and so doesn’t release ieo.

I’m not familiar with programming sio, but maybe someone else could confirm thar bit of your code.

You could try printing a message before waiting for any interrupt, just to verify that piece of code separately.

You could also try just putting ret at the very start of print, and check if the ieo is released.

Mark

Douglas Miller

unread,
Aug 31, 2020, 8:07:56 AM8/31/20
to retro-comp
I think you have a conflict (overlap) between the RST 08 jump (3 bytes) and your CTC ch1 interrupt vector at 000AH.

Jacob Hahn

unread,
Aug 31, 2020, 9:51:17 AM8/31/20
to retro-comp
Douglas,

Thanks for pointing that out. It is overlapping like you said. I didn't think about that. I've moved the interrupt routine to 1Ah to give it room. I will try the code this afternoon, and if it doesn't work I'll try Mark's troubleshooting to narrow down the issue. 

Thanks.

Jacob Hahn

unread,
Aug 31, 2020, 4:21:00 PM8/31/20
to retro-comp
I'm happy to report that Douglas' solution worked and now the CTC acts like I'd expect. Thanks for the help.

Tom Storey

unread,
Sep 2, 2020, 2:52:40 AM9/2/20
to Jacob Hahn, retro-comp
I know youve fixed it, but Im looking through the code right now and I see you were loading the interrupt vector in the CTC with a value of 0x08, but placed your interrupt vector at 0x0A in ROM - two bytes away from where it should have been.

TBH Im surprised the assembler even allowed you to overlap things like that! What did it end up producing, did the interrupt vector location end up overwritten by 1 byte or did the JP TXA instruction end up overwritten by one byte?

--
You received this message because you are subscribed to the Google Groups "retro-comp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to retro-comp+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/retro-comp/5d84dbe6-d09c-408e-826a-a4c6085c35a8n%40googlegroups.com.

Douglas Miller

unread,
Sep 2, 2020, 7:33:19 AM9/2/20
to retro-comp
For the Z80-CTC, the interrupt vector is the *base* vector, each channel has it's own vector. It is essentially the base address of an *array* of vectors. The channel being used to interrupt is channel 1.

Most assemblers will handle overlaps by simply processing them in the order they appear in the source file. So, the ".ORG 0AH" resets the load pointer to overlap the last byte of the jump at 08H, so the jump for RST 08 is "corrupted" and causes the program to essentially crash when a RST 08 is executed.

Tom Storey

unread,
Sep 3, 2020, 3:20:54 AM9/3/20
to Douglas Miller, retro-comp
Ah yeah, forgot about that little detail. facepalm. And I did a project with a CTC recently - crazy how quickly you can forget things. :-(


--
You received this message because you are subscribed to the Google Groups "retro-comp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to retro-comp+...@googlegroups.com.

Jacob Hahn

unread,
Sep 4, 2020, 2:27:35 PM9/4/20
to retro-comp
Tom,

I do find it weird that the assembler doesn't warn you about overlapping code. Do you know of any reason for this?

Thanks,
Jacob

Douglas Miller

unread,
Sep 4, 2020, 3:47:34 PM9/4/20
to retro-comp
That's a very common behavior of assemblers. Re-ORGing is quite common, and normal.

Tom Storey

unread,
Sep 7, 2020, 5:21:17 AM9/7/20
to Jacob Hahn, retro-comp
On Fri, 4 Sep 2020 at 19:27, Jacob Hahn <jto...@gmail.com> wrote:
Tom,

I do find it weird that the assembler doesn't warn you about overlapping code. Do you know of any reason for this?

Which assembler are you using? Ive been using zasm (https://k1.spdns.de/Develop/Projects/zasm/Documentation/). I will need to make a test case to see if it complains about things that overlap although my feeling is that it will - it was written by a German afterall. ;-)

Move the origin around in random order, sure, but it feels very wrong to me that an assembler should allow thing to overlap. You could end up overwriting critical code or data tables and introducing bugs. Depending on the application, either of those could be catastrophic!

Douglas Miller

unread,
Sep 7, 2020, 8:51:00 AM9/7/20
to retro-comp
Keep in mind that assembly language is very low level, nuts-and-bolts, programming. Traditionally, assemblers did not complain about things like this. Overlapping ORGs were often used intentionally for various purposes like defining constants and structure offsets. While actually generating code (vs. generating addresses) that overlaps is harder to justify, the general philosophy of assembers has traditionally been to assume the programmer knows what they are doing. Neither Digital Research CP/M assemblers nor modern 'zmac' complain about this overlap. I can't, off the top of my head, recall code that depended on this kind of overlap. But, my gut says there could be legitimate uses for it.

GNU 'as', however, does complain (fatal error) about any re-ORG backwards (regardless of whether it actually causes overlap or not). Of course, that's a completely different world. GNU clearly defines this behavior, and does not need to remain backward-compatible with 40-year-old technology or code.


On Monday, September 7, 2020 at 4:21:17 AM UTC-5  wrote:

Tom Storey

unread,
Sep 7, 2020, 2:10:55 PM9/7/20
to Jacob Hahn, retro-comp
Yeah, so zasm definitely complains if things overlap, and it also doesn't like if you try to go backwards with your origins. Here's the test:

#target ROM
#code ROM
    .org 0x8
    jp  test

    .org 0xa
    nop

    .org 0x20
test:
    nop

    .org 0x10
test2:
    nop

And this is the result of trying to assemble it:

$ ./zasm test.asm

in file test.asm:
6:     .org 0xa
               ^ gap size < 0
13:     .org 0x10
                 ^ gap size < 0
assembled file: test.asm
    16 lines, 2 passes, 0.0015 sec.
    2 errors

So, pick your poison I suppose - trust yourself not to overlap your code etc, or go with an assembler that will call you out if you try to do it. Personally I'd go with the latter - I'd rather not have to spend any amount of time wondering why something isn't working because one byte of an instruction has been overwritten and my jump goes somewhere completely random. :-)

Douglas Miller

unread,
Sep 7, 2020, 2:25:35 PM9/7/20
to retro-comp
Yes, the more-modern assemblers may be more restrictive. Were those warnings, or did you still get code produced?

Back in the old days, we did not use ORG so heavily, perhaps because it was prone to mistake. We tended to intentionally fill to the desired address, or else used something like the REPT macro to fill (or a computed "define storage" pseudo-op). These other methods would tend to produce errors on overlap, but you could also use "if" to force an error if the target address was over-shot.

As evidenced by GNU as, modern assemblers tend to be "forward only". I do notice that the legacy assemblers, which usually produce HEX files, actually produce output that has all the data (both overlapped and overlapping data). It would then have been up to the loader to detect the overlap.

Tom Storey

unread,
Sep 7, 2020, 2:43:54 PM9/7/20
to Douglas Miller, retro-comp
No actual output, although I dont think I had specified an output file. I tried it again, and it produced a listing file with opcodes, and also with the errors included, but still didnt produce a binary. And it seems the jump has some other random address too, so probably it hasnt done all of its passes yet, just the first to size everything up and recognise that things arent right.

--
You received this message because you are subscribed to the Google Groups "retro-comp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to retro-comp+...@googlegroups.com.

Douglas Miller

unread,
Sep 7, 2020, 2:50:10 PM9/7/20
to retro-comp
I also tried Microsoft M80, it is the same as DRI assemblers (silently ignores the backward ORG, produces code).

I guess it comes down to how the modern assembler is positioned - to be able to assemble legacy code (backward compatible), or only new code.

Fred Weigel

unread,
Feb 19, 2021, 7:01:59 AM2/19/21
to retro-comp
 In 1978..1979 we used a patched INTEL 8080 assembler similar to MAC (no linking, macros). Used a file that contained

  ROUTINE EQU nnnnH
  ROUT2 EQU nnnnnH
  ...
  ROUTX EQU nnnnnH

And then the source assembled looked like:

  ORG ROUTINE ; DEFINE ROUTINE
  ...
 CALL ROUT2
...
ORG ROUTX
...
 CALL ROUTINE

This using no linker -- the master file controlled the placement. A listing could be inspected to determine what
the actual placement should be. We then argued over sizes before coding. Put a 256x256 grid on the wall
which was the memory map for the project (8080 word processor, micom (philips) p2000d

Separate PASS1/PASS2 assembly, and then merge together INTEL HEX files to build load images.

FredW

Reply all
Reply to author
Forward
0 new messages