Hello, I'm Restarting ZX81 Programming After... 30 Years

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
IanB
Posts: 60
Joined: Mon Jul 27, 2015 5:40 am
Location: Northampton UK

Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by IanB »

Hi, there is not an "introductions" section on this forum so I am saying hello and asking some questions here, if that is okay.

I got a ZX81 (16K) in 1981, it was my first computer, when they seemed amazing just to exist at all. I became quite advanced (moderately) at programming machine code at the time, trying to squeeze as much as possible out of it, even programming things like interrupt service routines, etc. But eventually as time went on I lost interest, and moved on, so haven't done any programming on an '81 since about 1984 at the very latest :) I later got a Spectrum before eventually moving on to PCs after a break in the middle with nothing at all.

A couple of days ago I suddenly got a bizarre urge to program the '81 again. Unfortunately I no longer have mine, so for now an emulator will have to do. I've remembered a surprising amount of Z80 and the machine- some addresses and system variable names seemed to have burned into my memory forever(!) but I am still rather rusty of course. I hope I can ask some questions here as I progress.

I've also set myself the task of writing for the 1K (unexpanded) version (even though I had 16K in the 80s) since this seems a great challenge, having to optimise code by size when every byte counts (I even got lazy with that on 16k, which seemed so much RAM then, haha). I've got a project in mind but don't want to say what it is in case I fail :)

I'm trying to remember the weird hardware and the internet is some help of course. It seems people on this forum have a very good knowledge, so I'll start with a couple of questions-

Firstly, I know that af', ix and iy are off limits due to the hardware, but can I use the alternate registers bc', de' and hl''?

Another thing I am contemplating is that for my idea, I want to have my code do something on each TV frame, then wait for the screen refresh, then continue, etc. I'm not sure if this is possible, I'm wondering if I can end my code with a HALT followed by a jump back to the start of the loop, that will be triggered by the NMI when the screen starts refreshing? I don't know if I'm being clear here, I'm trying to sychronise my code with the display. Is this going to work?

Anyway, if anyone has any answers, thank you. And hello!
dr beep
Posts: 2082
Joined: Thu Jun 16, 2011 8:35 am
Location: Boxmeer

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by dr beep »

Hello IanB,

You came to the right address. I started coding hires game in just 1K in 2011.

You can use alternate registers an yes, IX IY and AF' are best avoided (Useable during intruptroutine possible).

To wait for 1/50th of a second you can always compare the value of FRAMES like this

Code: Select all

Ld hl,frames
Ld a,(hl)
Loop:
Cp (hl)
Jr z,loop
When you run MC only you can use your screen initially as the starttroutine for your MC. Also a lot of sysvar can be used as normal coding. However not directly, but i.e. as a buffer of data used in your program.
IanB
Posts: 60
Joined: Mon Jul 27, 2015 5:40 am
Location: Northampton UK

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by IanB »

Hi dr beep, thank you for your reply.

When does FRAMES get updated relative in time to the screen display? I'm hoping to have my code run immediately after the screen has been generated. IIRC, FRAMES update and keyboard reading is done by the maskable interrupt service routine, but I'm having trouble finding from the internet exactly when that occurs.
dr beep
Posts: 2082
Joined: Thu Jun 16, 2011 8:35 am
Location: Boxmeer

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by dr beep »

It doesn't matter when FRAMES is altered.
The ROM-display will finish with the result of FRAMES altered. After finding that FRAMES is altered you will have the maximum time to alter the screen without flashing display.

This might be usefull.
http://www.wearmouth.demon.co.uk/zx81.htm
dr beep
Posts: 2082
Joined: Thu Jun 16, 2011 8:35 am
Location: Boxmeer

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by dr beep »

You could also use IX to start your own routine before doing the display from the ROM. I use a lowresdisplay and hiresdisplay in my own routine. In this routine you can set the next to do thing for the next intrupt as well.
IanB
Posts: 60
Joined: Mon Jul 27, 2015 5:40 am
Location: Northampton UK

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by IanB »

I'm still a bit confused. This might be the gap of thirty years :)

So far as I can tell from the internet, the sequence appears to go something like this-

1) ISR creates VSync pulse, reads keyboard, updates FRAMES
2) Application execution
3) Display routine (NMI gubbins)
4) Application execution
[loop]

So there are two periods of program execution, one in the blank lines above the screen and one in the blank lines below the screen, with the ISR and FRAMES update (etc) in between them. If I read Frames, my code will start in (2) whereas I think it should start in (4) so that it has the most time to do stuff before the screen starts redrawing again. Does this make any sense, or have I misunderstood?

Regarding IX, I take it that I could re-vector that to a routine other than the standard display routine? I think that's a bit beyond where I want to go at this stage.
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by PokeMon »

Yes - you can use IX to build up your own video routine which may use the video rom routines partly.
If you just use IX to do something you shouldn't do too much as this would affect the video timing as well.

Yes - your program is executed two times per frame, for the top margin lines and for the bottom margin lines.
So you could calculate the available time clock ticks as follow:

About 148 clock cycles (43 are lost with NMI execution and around 16 clock cycles during WAIT from 207 all-in-all) * 2 * 55 margin lines * 50 frames

So this is about 16000 clock cycles available per frame or 810000 per seconde which corresponds with the typical speed determined with CLKFREQ.P as effective clock frequency. I wouldn't take too much care if your program is executed at the top or bottem margin lines - anyway you wouldn't see really a difference with your eyes. If you want to skip frames or waiting for the next one you could use the hint from dr. beep while checking the FRAMES variable.

Good luck ! 8-)
User avatar
RetroTechie
Posts: 379
Joined: Tue Nov 01, 2011 12:16 am
Location: Hengelo, NL
Contact:

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by RetroTechie »

Welcome to this forum, IanB ! :D And even better to have another programmer on board.
IanB wrote:So far as I can tell from the internet, the sequence appears to go something like this-

1) ISR creates VSync pulse, reads keyboard, updates FRAMES
2) Application execution
3) Display routine (NMI gubbins)
4) Application execution
[loop]
Yeah, that's pretty much it. If I'm not mistaken, 2) and 4) are about equal length, as they involve an equal # of (blank) display lines that are counted from the same system variable. Quoting from the readme in my own ULA project:

"The number of lines on top & bottom is taken from a system variable (MARGIN, address 16424), this allows adjustment for 50 Hz (PAL) or 60 Hz (NTSC) display. The system variable is set by reading bit 6 of I/O port FEh (ULA pin 22)"

The normal values for MARGIN are 31 or 55. Using some hardware attached to the expansion port, I once managed to count the # of clock cycles in a single frame, got a number of 64170/64171 (50 Hz). Frame lines are normally 207 cycles long (3.25 MHz Z80 cycles that is, pixels are pushed out at double that rate). Which allowed calculating the duration for 1) in # of lines:

MARGIN = 55 (for PAL TV's)

8 vsync / keyboard read
55 program execution
192 screen display
55 program execution
---
310 screen lines/frame, taking 310*207 = 64170 clocks -> frame rate = 3.25e+6 / 64170 = 50.6 Hz

So with MARGIN = 31 (for NTSC TV's), we then get:

8 vsync / keyboard read
31 program execution
192 screen display
31 program execution
---
262 screen lines/frame, taking 262*207 = 54234 clocks -> frame rate = 3.25e+6 / 54234 = 59.9 Hz

This assumes that 1) takes an exact # of screen lines, which I think is required to provide a stable horizontal sync frequency for TV's to lock on to.
So there are two periods of program execution, one in the blank lines above the screen and one in the blank lines below the screen, with the ISR and FRAMES update (etc) in between them. If I read Frames, my code will start in (2) whereas I think it should start in (4) so that it has the most time to do stuff before the screen starts redrawing again. Does this make any sense, or have I misunderstood?
The timings breakdown above holds one clue: if you can do all screen updating within ~5400 clock cycles (and sync that to the ZX81's display routine somehow), then you'd be safe both on PAL and NTSC ZX81's, regardless whether that update is done in the top or bottom part of each frame. That # of clock cycles = 31 * (207 - duration of line counting routine in the ZX81 ROM).

Oh ehh... EDIT:
PokeMon wrote:About 148 clock cycles (43 are lost with NMI execution and around 16 clock cycles during WAIT from 207 all-in-all)
So in that case the above would be ~4600 clock cycles.
Regarding IX, I take it that I could re-vector that to a routine other than the standard display routine?
Indeed there are some way(s) to hook into the display routine. Or even do your own. :?: I guess the ZX81 ROM would be the final reference there, but others may have better info. I'm mostly a hardware guy, and don't understand too much of ZX81 inner workings on the software side... ;)
And Dr. Beep is our resident expert when it comes to squeezing things in 1K (hi-res included). :ugeek:
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by PokeMon »

The screen won't be corrupted when you just start your program alone and let it be interrupted from NMI's. This is the easiest way and if your program is too slow it would just take more than 1 frame to run. Developing own display routines is a hard job and I would avoid this as beginner. So just start your program with RAND USR ..... and let the "OS" do the timing job. ;)

In fact would the NTSC version have much less program time with 2x31 lines per frame but resulting 60 frames. This gives 3720 horizontal lines in NTSC and 5500 horizontal lines with PAL which makes ZX81 50% faster than NTSC.

The available time per horizontal line can be calculated as follows:
207 clock cycles all in all minus
11 clock cycles for NMI (is a RST instruction with another value than other RST's - $0066)
32 clock cycles for NMI routine execution
around 16 clock cycles WAIT time

Difficult and variable is the WAIT time. As soon as the NMI line goes down the WAIT signal is active and this pulse is 5.5us long which is 18 clock cycles long. The wait will be accepted in any M cycle which maybe 3 to 5 clock cycles long, so in the middle 4 clock cycles. it is accepted only during T2 which could gives 2 cycles to hit the sampling point - so in the middle you can calculate 16 lost clock cycles (14-18) here. If you do the "WHY WAIT" mod from Wilf Rigter this gives much additional speed as the WAIT is technically needed only at the last NMI cycle and to be more precise only at the last NMI cycle of the top margin. Sinclair did a q&d solution with hitting wait every NMI cycle due to the easiest implementation way.
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: Hello, I'm Restarting ZX81 Programming After... 30 Years

Post by PokeMon »

RetroTechie wrote:MARGIN = 55 (for PAL TV's)

8 vsync / keyboard read
55 program execution
192 screen display
55 program execution
---
310 screen lines/frame, taking 310*207 = 64170 clocks -> frame rate = 3.25e+6 / 64170 = 50.6 Hz
To a bit more precise - and I studied this process quite long time when developing the ZXmore - it is following:

7 lines vsync/keyboard read while the whole pulse is 400us long with several preceeding and followed instructions given an execution time of about 448 us (a bit more to be sure to switch on NMI when 7 nmi period is passed).
55 lines program execution
1 line sync time (mostly with WAIT low)
1 line display prepare
192 lines screen display
55 lines program execution
1 line sync time (mostly with WAIT low)

Gives 312 lines per frame. Not just nit-picking - it is quite useful to know when what is done and what is to be used for. ;)
And by the way - the first of the 55 margin line is shorter (for the user program).
So its maybe 54.5 lines execution time.
Post Reply