UK Vintage Radio Repair and Restoration Powered By Google Custom Search Vintage Radio and TV Service Data

Go Back   UK Vintage Radio Repair and Restoration Discussion Forum > Specific Vintage Equipment > Vintage Computers

Notices

Vintage Computers Any vintage computer systems, calculators, video games etc., but with an emphasis on 1980s and earlier equipment.

Closed Thread
 
Thread Tools
Old 1st Jun 2019, 11:47 pm   #1
JohnBHanson
Heptode
 
Join Date: Aug 2009
Location: Worthing, Sussex, UK.
Posts: 661
Default Fun with Z80 Assembler

I much prefer Z80 over 6502 four things I hated about 6502.

1. Most programming in assembler required knowledge of what was free in page 0 -
different for each machine.
2. Stack space limited to 256 bytes - difficult to write multi-tasking as stacks for *all* task and interrupt service routines need to be here.
3. Needs to be an IO page splitting up the memory map - cannot go at the top which is where the reset vector is and cannot go at the bottom as that is where page0 is.
4. Lack of 16 bit registers

Did any body have fun with the Z80 (PS I did).
JohnBHanson is offline  
Old 2nd Jun 2019, 5:59 am   #2
Mike Fulton
Pentode
 
Mike Fulton's Avatar
 
Join Date: Apr 2006
Location: 7 Miles North of Durham, UK.
Posts: 130
Default Re: Fun with Z80 Assembler

I did!
I started on the 6800 and progressed through 6502, 6510, Z80 to 68000 but like yourself I probably enjoyed programming the Z80 the most as it was so much simpler than I had been used to.
I wouldn't say I hated the 6502 though and I really liked some of the perifieral ICs for their flexibility...
Mike Fulton is offline  
Old 2nd Jun 2019, 6:12 am   #3
TonyDuell
Dekatron
 
Join Date: Jun 2015
Location: Biggin Hill, London, UK.
Posts: 5,188
Default Re: Fun with Z80 Assembler

I really started with the Z80, but was glad when I discovered the 6809 which I found a lot easier to design with and to program. The 6809 didn't have many of the 6502 dislikes metioned in the first post (it had 16 bit registers, 2 16 bit stack pointers, 'zero page' became direct page and you could set the top 8 bits of the address to anything you liked, etc)

On 6502/6800 systems it was not unheard-of to put a couple of I/O devices at the very start of memory (so they took part of page 0) as this meant you could use the shorter/faster page zero addressing mode to access them. Take a look at the HP9815 calculator, a 6800-based machine.

Anyway, as for the Z80, one thing I hated about it was the indexed addressing modes. To me it was plain 'wrong' for the 8 bit 'offset' to be fixed (one of the instruction bytes) and the 16 bit 'base' to be in a register.

There were a number of little-used features. The I/O instructions that used 'C' as the port address put 'B' on the upper 8 address lines, this meant you could easily have more than the normal 256 I/O ports. Similalrly during refresh time (when RFSH/ was asserted), the R register appeared on the bottom 8 address lines (and I never forgave them for only making that a 7 bit counter, the kludge to extend it to 8 bits was a mess) and I (interrupt vector) on the top 8 address lines. At least one of my designs, which didn't use vectored interrupts, used that as a memory paging register.

Of course there were also a number of undocumented instructions. In general anything that used 'H' or 'L' could be prefixed by 'DD' or 'FD' to use the appropriate half of IX or IY, for example.
TonyDuell is offline  
Old 2nd Jun 2019, 6:58 am   #4
JohnBHanson
Heptode
 
Join Date: Aug 2009
Location: Worthing, Sussex, UK.
Posts: 661
Default Re: Fun with Z80 Assembler

The Z80 an even work as a crt controller as well! - look at the ZX80. Uses the refresh register to count the line length!. A design that really does use the minimum chip count.

But in terms of programming I liked the conditional return instructions (eg ret nz) etc. I wrote a routine called marked that saved hl/de/bc registers of the calling routine and also arranged for the
ret from the calling routine to restore the registers (By inserting a different address in the stack
thus:-

Code:
SUBR:   call MARK ; save registers
             ....some code
            ret nz        ;conditional return and restore registers
            ...more code
            ret            ;unconditional return and restore registers
of course can do the same in other architectures - but with the Z80 conditional returns it adds extra value to these instructions (otherwise they would have to be conditional jumps to pop instructions.

I agree IX/IY instructions are also slow as well.

The IO chips are ok, do have some odd features. The SIO is sensitive to the order of initialisation and the PIO needs an M1 cycle to come out of reset, in addition the IO chips keep an eye on the instruction stream to look out for the RETI instruction. This is a cludge should be done with a different bus state. This also make hard to use with my bus extender (Which allows IO peripherals to be used with my emulator) - I have not cracked that one yet so I cannot use real IO cards that use a Z80 PIO with my Z80 emululator.

Still Z80 was affordable and I have had so much fun with it. I never had the opportunity to use the 6809.
JohnBHanson is offline  
Old 2nd Jun 2019, 8:40 am   #5
Duke_Nukem
Octode
 
Duke_Nukem's Avatar
 
Join Date: Jan 2003
Location: Birmingham, West Midlands, UK.
Posts: 1,268
Default Re: Fun with Z80 Assembler

I had some Z80 fun on a friends Sinclair - cant remember now if it was a ZX81 or a Spectrum (but what I do remember was it had seemingly very peculiar screen memory layout).

Quote:
(6502)Most programming in assembler required knowledge of what was free in page 0 - different for each machine.
When writing assembler in any environment you need to know what memory is free, though true it was more important when dealing with the 6502's 128 16-bit registers

I think Tony may have hit on why I found the Z80 frustrating at the time (as I couldn't remember the details):

Quote:
Anyway, as for the Z80, one thing I hated about it was the indexed addressing modes. To me it was plain 'wrong' for the 8 bit 'offset' to be fixed (one of the instruction bytes) and the 16 bit 'base' to be in a register.
For plotting things like sprites, I needed to index through a pixel mask table, the actual pixel table and arrange a plot offset (maybe with offset lookup from another table), that's three or four different indexes being managed at the same time. All hail the 6502

Of course that is a single specific use case. The Z80 was significantly quicker when I tried calculating Pi to an "unbelievable" several hundred decimal places !

TTFN,
Jon
Duke_Nukem is offline  
Old 2nd Jun 2019, 9:00 pm   #6
julie_m
Dekatron
 
Join Date: May 2008
Location: Derby, UK.
Posts: 7,735
Default Re: Fun with Z80 Assembler

I started with a ZX81, but never managed to get my head around Z-80 machine code. When I moved on to a BBC model B, with its built-in 6502 assembler, I found that processor's instruction set easier to learn. Later I went back to the Z-80 with a ZX Spectrum, now having a bit more of a grasp of what assembly language could do. And I had fun, eventually even modifying a ZX-81 general-purpose I/O card to work with the Spectrum. I used that to control a disco light controller I had made earlier and had the foresight to include a "computer port" (the inputs of four of the drivers in a ULN2803, which turned on the LEDs in the opto-isolators). And when I picked up an Interface One, I wrote a Z-80 cross-assembler in BASIC on my Amiga 500 and transferred code and data over the serial port! It was still quicker than loading and saving with cassettes (no Microdrive, unfortunately).
Quote:
Originally Posted by JohnBHanson View Post
I much prefer Z80 over 6502 four things I hated about 6502.

1. Most programming in assembler required knowledge of what was free in page 0 -
different for each machine.
2. Stack space limited to 256 bytes - difficult to write multi-tasking as stacks for *all* task and interrupt service routines need to be here.
3. Needs to be an IO page splitting up the memory map - cannot go at the top which is where the reset vector is and cannot go at the bottom as that is where page0 is.
4. Lack of 16 bit registers
Taking your points one by one:
  1. This is true, but it should be mentioned in your system documentation. If not, that's a fault of the computer manufacturer, not the chip.
  2. How much stack space do you really need? With only three main registers and one byte of status, you need only store at most 6 bytes of context on the stack including the return address. If you want a parameter-passing stack, you can implement it easily enough in software.
  3. This criticism also applies to the 6800. It was less of a problem in the 1980s when many home computers had less than 64kB of memory (ROM and RAM) anyway.
  4. That's what zero page is for! The 6502 has instructions such as LDA(&70,X) and LDA(&70),Y. The former reads the contents of &70+X and &71+X, and then reads the contents of that address into A. The latter takes the 16-bit value in &70 and &71, adds Y and reads the contents of this address into A.
I could counter by saying that the Z-80 plods along, taking too many cycles to do anything (the shortest instructions take 4 clock cycles, as opposed to the shortest on the 6502 which take 2 cycles). The index registers appear awkward, if you are already used to how the 6502 handles indirect addresses using zero page locations as auxiliary registers.

Basically, they're two different but equally-valid means to the same end. And at least we can both agree that units-first is the proper way to write a multi-byte number

@Jon -- the Spectrum has a bit-mapped screen with the address lines effectively out-of-order. (I believe it is actually using the periodic reads of display memory to refresh the low 16K of RAM.) The scanlines end up being stored in order 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25 ..... 39, 47, 55, 63, 64, 72, 80, 88 ..... 168, 175, 183, 191.
__________________
If I have seen further than others, it is because I was standing on a pile of failed experiments.
julie_m is offline  
Old 4th Jun 2019, 3:04 am   #7
Catkins
Pentode
 
Catkins's Avatar
 
Join Date: Feb 2015
Location: Chepstow, Monmouthshire, UK.
Posts: 234
Default Re: Fun with Z80 Assembler

Quote:
Originally Posted by Duke_Nukem View Post
I had some Z80 fun on a friends Sinclair - cant remember now if it was a ZX81 or a Spectrum (but what I do remember was it had seemingly very peculiar screen memory layout).
Then it was a Spectrum. The ZX81 only supported B/W character mode (text plus some character graphics blocks).

The Spectrum display consisted of a "high resolution" 256x192 display, with 1 bit per pixel, and a parallel 32x24 colour attribute grid. As you said the pixel display was oddly mapped, it was split into 3 separate blocks of 64 pixels in height (top, middle, bottom), and each block was organised where each successive 32 byte pixel line (32*8 = 256 pixels), jumped 8 rows, so the first 32 bytes was row 0, the next 32 bytes row 8, and so on, until it wrapped around, the 9th 32-byte line (8*32 = 256) being row 1, etc. Logically then for the top, middle, and bottom, the display frame organisation was all the first scan lines for each 8-pixel block, all the second scan lines for each 8-pixel block and so on.

To work out the byte position for an arbitrary Y (pixel row), you first worked out whether it was in the top, middle, or bottom, and then basically split it into two numbers representing: which 8-pixel block it was, and the line offset within that 8-pixel block. Armed with that you can work out where in the above organisation it will be:

1. Y REM 8 (Modulus remainder) gives the scan line offset, multipled by 256 gives a byte start position

2. Y / 8 (integer division) gives the 8-pixel block, multipled by 32, gives the byte offset from the above

Or

Address = (Y REM 8) * 256 + (Y / 8) * 32

Obviously, as the operations are all pure powers of 2, they can in practice be replaced by bit shifts.

But on a slow 8-bit processor this literally killed performance. But, once you worked out the byte position, you could make certain optimisations - you probably have noticed the next line down can mostly be found by adding 256. If your address was held in HL, moving to the next line could be achieved by incrementing H. So drawing a vertical line could be done by mostly incrementing H.

But in practice, dealing with the insane mapping was just too slow, despite any optimisations.

I wrote a wire-frame 3D graphics game for the Spectrum which involved drawing a lot of lines per frame, and, I discovered it was far better to draw into a "shadow display" which was sanely mapped, and then copy it to the real display area.

Quote:
Originally Posted by Duke_Nukem View Post

I think Tony may have hit on why I found the Z80 frustrating at the time (as I couldn't remember the details):

Quote:
Anyway, as for the Z80, one thing I hated about it was the indexed addressing modes. To me it was plain 'wrong' for the 8 bit 'offset' to be fixed (one of the instruction bytes) and the 16 bit 'base' to be in a register.
TTFN,
Jon
I hardly ever used the indexed addressing modes, because they were insanely slow, even slower than everything else.

The Z80 had 252 "basic" one byte operand op codes. Anything else, including the indexed addressing, used an opcode prefix byte. If you wanted performance, you simply avoided them.
Catkins is offline  
Old 4th Jun 2019, 4:15 am   #8
Catkins
Pentode
 
Catkins's Avatar
 
Join Date: Feb 2015
Location: Chepstow, Monmouthshire, UK.
Posts: 234
Default Re: Fun with Z80 Assembler

Quote:
Originally Posted by julie_m View Post
I started with a ZX81, but never managed to get my head around Z-80 machine code. When I moved on to a BBC model B, with its built-in 6502 assembler, I found that processor's instruction set easier to learn. Later I went back to the Z-80 with a ZX Spectrum, now having a bit more of a grasp of what assembly language could do. And I had fun, eventually even modifying a ZX-81 general-purpose I/O card to work with the Spectrum. I used that to control a disco light controller I had made earlier and had the foresight to include a "computer port" (the inputs of four of the drivers in a ULN2803, which turned on the LEDs in the opto-isolators). And when I picked up an Interface One, I wrote a Z-80 cross-assembler in BASIC on my Amiga 500 and transferred code and data over the serial port! It was still quicker than loading and saving with cassettes (no Microdrive, unfortunately)
I had two Microdrive units with my Spectrum. They were far more convienient than cassettes, until you invariably hit the dreaded "file not found", and you found you'd lost all your hard work. Cassettes were much more reliable than Microdrives, which stretched in use, and even snapped. I routinely ended up having to making multiple back-ups, on multiple microdrives, which was expensive.

My first experience with assembly language was the Motorola 6809 on the Dragon 32. Afterwards I moved onto the ZX-Spectrum with the Z80, which I found far more rewarding.

This was not because the Z80 was architecturally a better processor than the 6809, it obviously wasn't. The difference was the difference in design philosophy between the Dragon 32 and the ZX-Spectrum. The Dragon 32 was one of the last "hardware oriented" microcomputer designs, it was basically Motorola's reference design, using dedicated hardware chips for sound, display etc. Even by 1982 standards the graphics chip in the Dragon 32 was archaic. The ZX-Spectrum in contrast was a "software oriented" machine, very little was done in hardware, the vast majority of work was done in software.

This meant despite the relatively advanced 6809 processor in the Dragon 32, you constantly hit immutable constraints caused by the hardware. The ZX-Spectrum in contrast could be pushed beyond it's design by clever software.

You would see that distinction increasingly in later years. The Apple Macintosh was a pure software machine, hardware-wise it wasn't very fast or powerful, it was the software which made it what it was. Other designs, despite relatively comparable hardware, or often vastly better hardware, failed to sell because they were stuck in the hardware first, software second mindset, which meant little attention was placed on the software. The Sinclair QL was arguably decent-ish hardware let down by terrible software, mouse? graphics? Nope, we'll have text only and menus.

Quote:
Originally Posted by julie_m View Post
@Jon -- the Spectrum has a bit-mapped screen with the address lines effectively out-of-order. (I believe it is actually using the periodic reads of display memory to refresh the low 16K of RAM.) The scanlines end up being stored in order 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25 ..... 39, 47, 55, 63, 64, 72, 80, 88 ..... 168, 175, 183, 191.
Hmm, you may be conflating two things here

My memory is that the Z80 had the inbuilt ability to do memory refresh (the R or refresh register). This meant Sinclair didn't have to "waste" money on dedicated memory refresh hardware on the ZX81 and ZX-Spectrum.

The problem came with the next design - the SInclair QL. The M68008 (physical 8-bit data bus variant of the M68000, so architecturally the same, but vastly slower due to being hammered by the slow 8-bit data bus ) didn't have the ability to do memory refresh.

The obvious choice here was to just add some dedicated memory refresh hardware. Sinclair didn't want to do that. Instead it was decided to have the display hardware do the memory refresh for the standard 128K. While the display controller was doing a "refresh" the M68008 was locked out.

The result was huge contention over the already slow 8-bit data bus.

Frankly some of the design decisions on the Sinclair QL were insane.
Catkins is offline  
Old 6th Jun 2019, 10:08 pm   #9
Graham G3ZVT
Dekatron
 
Graham G3ZVT's Avatar
 
Join Date: May 2010
Location: Greater Manchester, UK.
Posts: 18,675
Default Re: Fun with Z80 Assembler

I taught myself Z80 assembler with the aid of two books I bought round about 1983 I prided myself that my "pinnacle of achievement" was a real-time slow-scan TV application on my ZX81, as you typed on the keyboard it fetched the bitmaps for the characters from ROM called subroutines for tones corresponding to black or white pixles, called the tone for line sync did that 120 times, then the coup de grace, 8 lines of an 8 level greyscale before calling the frame sync routine.

It generated a 8X8 character matrix and I was really pleased with it as it was achieved without reference to any prior art, but I was also frustrated because I didn't know anyone who I could bounce ideas with.

In truth my code was probably naive and inelegant. It taught me concepts like endianness and two compliment.

I sometimes wonder if with the right mentors I would have got into development and coding for a living, some unlikely types of my acquaintance did!
__________________
--
Graham.
G3ZVT
Graham G3ZVT is offline  
Old 7th Jun 2019, 5:18 am   #10
ricard
Octode
 
ricard's Avatar
 
Join Date: Mar 2006
Location: Lund, Sweden
Posts: 1,631
Default Re: Fun with Z80 Assembler

Quote:
Originally Posted by Catkins View Post
I had two Microdrive units with my Spectrum. They were far more convienient than cassettes, until you invariably hit the dreaded "file not found", and you found you'd lost all your hard work. Cassettes were much more reliable than Microdrives, which stretched in use, and even snapped. I routinely ended up having to making multiple back-ups, on multiple microdrives, which was expensive
Yeah, the Microdrive really hammered in the need to make backups. I did some fairly serious hobby software development back in the day, in Z80 assembler, with the source code stretched over a number of files. At least once a day the content of one of the Microdrive cartridges wouldn't read properly and had to be reformatted, necessitating reverting to the backup. I rarely had the tape snap on one though, but the design seemed very brittle - as was the design of the ZX Printer (probably one of the few consumer devices to actually enhance the ozone layer, with its stylus burning away the metal surface coating on the associated printing paper).

I cut my programming teeth at school at which they had a globally little-known Swedish computer called the ABC80. It was originally intended to be a clone of the American TRS80 but ended up as a completely different machine just sharing the form factor (keyboard/computer unit, and dedicated matching monitor). ROM BASIC, but it got really fun when I got into Z80 assembly language.

Eventually I got a used ZX80 at home, eventually upgraded to a ZX Spectrum which saw a lot of use. Eventually I replaced the keyboard with a 'real' one bought surplus which I wired up to behave like the Spectrum keyboard matrix. It looked really klunky all in all with this full-sized keyboard stuck on top of the space originally occupied by the original 'dead mans hand' rubber one, but it significantly increased typing speed.

I did all sort of hacks via the expansion port, with a MIDI interface, SP0256 speech synthesizer chip, 8-bit A/D and D/A converters (Ferranti ZN449 and 425 or something like that), 8 bit parallel interface primarily to interface with an analog drum synthesizer I'd built, an EPROM programmer, and finally a 64 kbyte memory which could be swapped in banks for the built in ROM. I eventually ported the aforementioned ABC80 ROM to the Spectrum for instance.

Initially I'd hand assemble the Z80 assembly language to the point where I got to know by heart (and, staggeringly, still do - I wonder how much space in my brain has been sacrificed by the apparent permanent burn-in of the opcode list) a significant part of the Z80 opcodes in decimal. After a while I wrote a line assembler which assembled one instruction at a time into memory, and the program also doubled as a machine-language monitor so I actually used it for debugging too. Eventually I wrote a fully fledged assembler which would take a number of source files and assemble them to one binary.

Weirdly, after the Spectrum I got a second-hand ABC80 at home, because it was a machine I knew well and used real 5.25" disk drives as opposed to the Spectrum's Microdrives. The graphics on the ABC80 were on par with the blocky ZX80/81 graphics, but by that time I was more interested in embedded systems so didn't care much for graphics. I constructed a polyphonic music synthesizer again using the Z80, and did the software development on first the Spectrum and then the ABC80.

Eventually the limitations of 8-bit systems started to become a real pain, especially when the Internet started to become a part of our everyday lives, and sometime around 1996 or so I succumbed to the PC world, buying a second-hand 486 PC. But I've never really got into assembly language programming on any other architecture; with the PC, of course, high level languages like C do a better job than hand coding assembly language for 99.9% of the tasks at hand, and also, the opcode sets of 16- and 32-bit CPUs are much more complicated, so it's not really feasible to know them by rote.

As a side note, one of the more esoteric uses of Z80 assembly language was when a friend of mine and me at university needed to translate some hexadecimal into decimal during a lab session. He knew the Z80 opcodes in hex, and I knew them in decimal, so we'd start out with '0C3h', he'd say 'JP' and I'd say '195'.

Last edited by ricard; 7th Jun 2019 at 5:26 am.
ricard is offline  
Old 7th Jun 2019, 6:12 pm   #11
SiriusHardware
Dekatron
 
Join Date: Aug 2011
Location: Newcastle, Tyne and Wear, UK.
Posts: 11,482
Default Re: Fun with Z80 Assembler

Quote:
Originally Posted by Catkins View Post

I had two Microdrive units with my Spectrum. They were far more convienient than cassettes, until you invariably hit the dreaded "file not found", and you found you'd lost all your hard work.
Uhh, tell me about it. About ten years ago I decided to un-store my Spectrum and its peripherals, including the Interface 1, Microdrive, and two 'proper' cartridge boxes filled with cartridges containing everything from classic games which I had modified to run from Microdrive, to 'Art' drawn with various art packages, to page after page of assembly language code, all of which must have taken thousands of hours of my life to create or otherwise transpose onto Microdrives.

When I tried them they were all, every single one, unreadable. It's well known that these suffer from the same 'pressure pad rot' which sometimes also afflicts cassette tapes, but in this case the pads were all OK and the tapes all seemed magnetically 'dead'. I was so put out that I threw all the cartridges away, and although I bought some replacements (with good pads) the loss of all that irreplaceable material killed any interest I might have had in revisiting the Spectrum at the time.

However, a fellow forum member has, just in the past week, brought my attention to this:

https://vdrivezx.com/vdrivezx/

It's a PCB which fits inside an original Microdrive housing and is, as far as the Interface 1 is concerned, an original Microdrive - but it 'records' to SD card - the card holder receives the card through the original front slot in the microdrive unit, very neat. I'll be getting one of these myself sometime in the next few weeks - I may post a 'review' in a separate thread.

There is a separate, similar version available for the QL.

As if mere unreliability wasn't bothersome enough, another great thing that the Spectrum / Microdrive combo would sometimes do was to crash just at the point where the Microdrive was recording something. If you were unlucky, it would erase the entire contents of the cartridge before you had time to realise what was happening.
SiriusHardware is online now  
Old 7th Jun 2019, 7:21 pm   #12
Graham G3ZVT
Dekatron
 
Graham G3ZVT's Avatar
 
Join Date: May 2010
Location: Greater Manchester, UK.
Posts: 18,675
Default Re: Fun with Z80 Assembler

Some of the top executives at the place where I was working had OPD "One Per Desk" computers on their desks. They just seemed to use them as expensive telephones, and had no interest in the pair of Microdrives they sported.

I thought I might have found a source of free Microdrive tapes for my Sinclair, but alas it came to nothing.
__________________
--
Graham.
G3ZVT
Graham G3ZVT is offline  
Old 7th Jun 2019, 9:00 pm   #13
Catkins
Pentode
 
Catkins's Avatar
 
Join Date: Feb 2015
Location: Chepstow, Monmouthshire, UK.
Posts: 234
Default Re: Fun with Z80 Assembler

Quote:
Originally Posted by SiriusHardware View Post
As if mere unreliability wasn't bothersome enough, another great thing that the Spectrum / Microdrive combo would sometimes do was to crash just at the point where the Microdrive was recording something. If you were unlucky, it would erase the entire contents of the cartridge before you had time to realise what was happening.
Arrgh I'd forgotten about that, but you're right, that was also another great feature.

I always assumed the crash was because the Z80 jumped into machine code in the ROM on the Interface 1, and if any of the edge-connector pins waggled, it started executing rubbish (aka as going into hyper-space) with the microdrive hardware in record mode.
Catkins is offline  
Closed Thread

Thread Tools



All times are GMT +1. The time now is 11:18 am.


All information and advice on this forum is subject to the WARNING AND DISCLAIMER located at https://www.vintage-radio.net/rules.html.
Failure to heed this warning may result in death or serious injury to yourself and/or others.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Copyright ©2002 - 2023, Paul Stenning.