HGFX - ZX Planar

Field Programmable Gate Array based devices! As exciting as they sound
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

HGFX - ZX Planar

Post by SamC »

In several next posts, a basic introductory information about a new hardware add-on, called HGFX, will appear. This is something that is not quite common on eight-bit computers, so-called "planar" graphics with a large number of colours. The authors took a great care to ensure that this system is perfectly adapted to our ZX Spectrums, compatible with the classic attributed graphics, its memory organization. They shaped it also for game conversions.

The HGFX system is a fast system suitable for our slower eight-bit CPUs. It is available both in the LnxSpectrum emulator and in two modern FPGA platforms - eLeMeNt ZX and MB03+ Ultimate. Hopefully these FPGA cores will be transfered to other modern clones soon (e.g. MEGA65).

The following introductory series is of basic level. I am not a hardware-man. However, in later contributions, I will present to you more detailed examples, graphics or small videos with a short description of the functions and important properties used.

Image

HGFX is the result of a hard work of two people, Lanex and LMN128, with a moral support from me and my friend Hood. I want to thank my friends for their great help with all details and explanations you will read here.
I also want to thank them for the brotherhood that allowed us to tune various ZX Spectrum hardware for the 21st century. This helped to merge different approaches (clones, peripherals), west- and east- spectristic styles and manners. With the slogan: Don't exclude proven old things and moreover, combine new and good things. Please look at www.128land.com for an overview of developments over the past few years.

Finally, I would like to thankyou for support to everyone who contributed with their hands and heads, advices or other support, they simply gave this new hardware of the eLeMeNt ZX/MB family a chance: pvym, Zoom, Busy, lordcoxis, nihirash, NEO SPECTRUMAN, Tchunass, Velesoft, Martin8bity, dakidski and others
+ applause belongs to the esxDOS developers community, Without them our efforts would not have been successful!
Last edited by SamC on Fri Jul 22, 2022 7:53 pm, edited 1 time in total.
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

HGFX is a new hardware expansion that adds a "planar" graphics to the ZX Spectrum graphics screen. It uses a normal ZX-screen bitmap of size 6144 bytes (from address 16384), but enables to colour each individual point (pixel) as you like. You can keep original ZX-pixels and put an additional colour graphics to them, of course.

You might know a planar system from some game systems and home computers. In the case of our ZX Spectrum, this new graphics did not needlessly occupy a small and mostly fully used memory space of 48 to 128 KB. There is no need to write or move large sections of memory with the HGFX planar system. While so-called "chunky" colour graphic routines operate a whole one byte by a 256coloured pixel, orders for HGFX operations are dealing with one bit only.

HGFX has been carefully developed with regard to a standard ZX Spectrum (graphics and memory) compatibility, e.g.

- the HGFX videoram address can be put at the ZX-screen
- the videoram can be operated simultaneously with the ZX-screen, which is displayed in place of the transparent HGFX colour
- conversion of programs and games for HGFX does not require changing the memory arrangement, videoram still has only has 6144 bytes of memory
- all new HGFX features can be used even with standard (non-linearly stored) ZX-screen format

Briefly about the HGFX system

HGFX manages eight sections of internal memory, so-called "bitplanes", each with a size of 6144 bytes. They are stored one behind the other, but for a better clarity, let's imagine them stacked on top of each other (see ascii-picture). The more bitplanes you plug into a system, the greater "depth" of colours you will achieve. One bitplane allows two colours, so pixels can only turn on or off (black or white). Two bitplanes provide 4 colours. 4 bitplanes 16 colours etc.

The fact that the entire pixel is not stored in one place, paradoxically provides a number of advantages when working with an extended graphics especially on a small ZX Spectrum.

Code: Select all

HGFX Planar
Graphics
               --------------------------------------
              /                                    /
             /                                    /--   
 Bitplane   /                                    / /    Colours
           /                                    / /--
          /                                    / / /       2
 no.0    -------------------------------------- / /--       
          /                                    / / /       4
 no.1    -------------------------------------- / /--
          /                                    / / /       8
 no.2    -------------------------------------- / /--
          /                                    / / /      16
 no.3    -------------------------------------- / /--
          /                                    / / /      32
 no.4    -------------------------------------- / /--
          /                                    / / /      64
 no.5    -------------------------------------- / /      
          /                                    / /       128
 no.6    -------------------------------------- /
          /                                    /         256
 no.7    --------------------------------------
Each pixel has 1 bit reserved in individual bitplanes. This is similar to POKE 16384,1 on the ZX Spectrum, which draws a single point on the screen. The difference is that the HGFX does not work with "monochromatic" graphics of one bitplane only, but ensures that a request for a colour pixel - or another effect - is rewritten to all bitplanes. It happens very quickly and a colour character print is as fast as a printing on the original zx-screen. Technical in short, with one write to a given pixel position, HGFX will set all of 8 bitplanes.

Now you might be wondering how the colour value of the pixel is overwritten in bitplanes? Let's illustrate a specific colour scheme pixel value in one byte, in a binary form, and - for bitplane purposes - let's put it upright, so we can better understand how it is written by the HGFX system, i.e. "overwritten" across bitplanes.

Code: Select all

HGFX Pixel
Colour Value
                decimal: 25
                binary: 00011001

  bits order:  0 0 0 1 1 0 0 1   <-- The lowest bit goes
    bitplane:  7 6 5 4 3 2 1 0   <-- to the 1st (top) bitplane.

            /                                    / /
           /                                    / /--
          /     1                              / / /  
 no.0    -------------------------------------- / /--       
          /     0                              / / /
 no.1    -------------------------------------- / /--
          /     0                              / / /
 no.2    -------------------------------------- / /--
          /     1                              / / /
 no.3    -------------------------------------- / /--
          /     1                              / / /
 no.4    -------------------------------------- / /--
          /     0                              / / /
 no.5    -------------------------------------- / /      
          /     0                              / /  
 no.6    -------------------------------------- /
          /     0                              /
 no.7    --------------------------------------

                The most upper bit of the colour value
                goes to the lowest (last) bitplane.
Last edited by SamC on Fri Jul 22, 2022 8:29 pm, edited 10 times in total.
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

HGFX Intro, part 2

Drawing a coloured pixel

We already know that for handling planar memory and for drawing pixels, we don't need to handle each bitplane individually. The HGFX will manage it for us, with a simple setup:

1. We set the colour of pixels.

The HGFX can display 256 colours at once. We set a number of the colour in the colour index (a value from 0 to 255). The colour index is a table in the ZX Spectrum memory, the table length is 768 bytes and contains 256 positions. The colour is always given by three bytes because it is set from a range of a true-colour palette, i.e. 16777216 colours, as in the HRXC mode (HiResindeXedColour).

2. Next we set a PlanarMask value.

This is a number that indicates which bitplane will be affected by a pixel rendering operation. The active (set) bits of the PlanarMask number indicate which bitplane to work with and which bitplane is for current pixel printing "off". So it is a mask and through this mask relevant bits of colour will be to "poured" into the individual bitplane.

The PlanarMask actually determines a depth of the colour application. 8 bits out of 8 of bitplanes are projected into one single pixel on the screen. It is the third dimension of graphics, a sort of Z-axis.

Examples: We will draw a pixel with the colour (IndexColour) number 233 by setting the value 255 (binary 11111111) in PlanarMask. The number 233 is actually binary ANDed with the number 255 in HGFX. All active bits of the mask will cause the value of the colour will be projected to all bitplanes.

A more complex is an example in which we put, by changing the value of the mask, a colour value only to selected bitplanes: in the below example, we want to write the value only in the the first four bitplanes (bitplane 4 to 7) and the last (bitplane no. 7).

Code: Select all

      Old colour:
   (bitplanes before the change)   145 dec, 10010001 bin			
      New colour:                  233 dec, 11101001 bin			
      Mask:                        143 dec, 10001111 bin


      New           Planar     Bitplanes     Bitplanes
      Colour        Mask       before        after

  b0    1   ----->   1  ------change------>    1
  b1    0   ----->   1  ------change------>    0
  b2    0   ----->   1  ------change------>    0
  b3    1   ----->   1  ------change------>    1
  b4    0   ---X--   0            1  ----->    1
  b5    1   ---X--   0            0  ----->    0
  b6    1   ---X--   0            0  ----->    0
  b7    1   ----->   1   -----change------>    1      

    (abbreviations b0..b7 mean both bit and bitplane)


Expressed in binary form:

      New colour:     11101001
      Mask:           10001111
      ------------------------
      Old colour:     10010001
      ========================
      Result          10011001

3. And now we are drawing a pixel.

Very simple, we have already set the colour (IndexColour) and the "masking" value for bitplanes (PlanarMask), so all that's left is to tell the HGFX to "activate" the pixel.

For this purpose we will use the HGFX videomemory of 6144 bytes. Its content is not different from what we already know when we programmed for the ZX Spectrum. Let's imagine it as a classic ZX-screen, a matrix of 256*192 pixels, i.e. 32*192 bytes, where each individual bit represents one pixel. By writing to the videomemory, by changing individual bits, we talk to HGFX which pixels to operate with.

For the ZX-screen, the POKE command 16384,15 causes a row of 4 dots will light up in the upper left corner of the screen (in colours of ZX-attributes). If we turn the HGFX on, the same command wiil draw 4 points in full 256 colours.

A compatibility with the ZX-screen is important. All entries into the classic videoram, both zx48 and zx128, every pixel drawing in the ZX-basic and in the machine code can easily be coloured with a help of the HGFX, with one from 256 unique colours (or another effect can be added]). The HGFX does that just by changing one bit in videoram.

However, the HGFX can also ensure that a new graphics comes together with the ZX-screen. A HGFX videoram location address can be at any time to changed and moved to another place in a memory, for example to address 0 if you have all-ram mode enabled in the ROM location (we can also move the colour table).

In this part, for simplicity, we have drawn only one coloured pixel, you will surely think the planar mask is also suitable for other, more complex operations. We'll talk about that later.
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

In the third and fourth parts of the HGFX introduction, let's skip the theory of the planar, we will talk about a few nifty details of this graphic expansion.

1. Nonlinear vs. linear memory layout.

The Spectrum's very special way of videoram format is pretty noticeable when a ZX-screen is slowly loaded from a tape. The lines are placed in thirds of the screen, in the order of lines 0, 8, 16, ... to 56, followed by lines 1, 9, ... to 57, etc.

Code: Select all

    ZX Spectrum bitmap structure

        1st line  ...  byte 0 to 31  
        2nd line  ...  byte 256 to 287 
        ...
        8th line  ...  byte 32 to 63
        9th line  ...  byte 288 to 319
        ...

To make HGFX easy to use with almost every existing one ZX Spectrum program, this original non-linear arrangement of a videoram is set by default in the HGFX. In case we don't need a ZX-screen compatibility, let's try a linear memory format. It will enable fast time-saving operations such as copying parts of the screen and DMA transfers (shifts of a screen by one pixel, for example).

Code: Select all

    HGFX Linear video memory

        1st line  ...  byte 0 to 31  
        2nd line  ...  byte 32 to 63
        3rd line  ...  byte 64 to 95
        ...
2. Transparency

The original ZX-screen with attributes can be preserved simultaneously with the HGFX turned on. If we enable the transparency, the ZX-screen will be displayed under the HGFX layer and will only be visible at places where the index color no.0 is set in the HGFX graphics.

With the HGFX videoram set by default, from 16384, we draw simultaneously to the ZX videoram and to the HGFX video memory. HGFX diplay buffers have a priority though. If the transparency is not turned on, the HGFX will cover the ZX attributed graphics with its colour.

Keeping the ZX-screen in parallel with the HGFX will show its magic when patching programs and games for the HGFX. Not all graphic elements, however, are needed to be converted to the HGFX (e.g. a game background) and it could be also very laborious.


3. Offsets


Offsets are certainly remembered by those who programmed in the Beta Basic and used the system variables XOS and YOS. The rest of us are let's remember the simple definition: an offset sets a drawing position on the screen. The X and Y coordinates in pixels are in the HGFX system given as follows:

Code: Select all

---------------------------------------------------------------
| X,Y: 0,0                                        X,Y: 255,0  |  
| adr. 16384                                      adr. 16415  |  
|                                                             |
|                256 pixels, 32 bytes per line                |
|                                                             |
|                                                             |
|                                                             |
|       192 lines                                             |
|          192*32 = 6144 bytes                                |
|                                                             |
|                                                             |
|                                                             |
|                                                             |
|                                                             |
|                                                             |
| X,Y: 0,191                                      X,Y: 255,191|
| adr. 22496                                      adr. 22527  |  
---------------------------------------------------------------

Example 1:

We set OffsetX=0, OffsetY=16 and print a character to the videoram. We store the first byte at address 16384, the next byte at 16416 (16384+32), etc. A character will appear on the screen as printed using basic's PRINT AT 2.0;

Example 2: Printing letters 6 pixels wide.

We set offsets to 0,0. We print classically (as in the first example) at address 16384. Coordinates for the second letter we will also set by an offset. Set OffsetX to 6, and print... again to the address 16384 (?! yes, a method to transform coordinates into bytes and bits of the exact location in memory does really NOT apply, even character bit templates don't need to be shifted!). For the third character, we set OffsetX to 12 and print again to 16384. This is how we proceed this with all characters.

You can certainly imagine other examples yourself. Time savings of programs are tremendous when using offsets. Whether it's a job of proportional fonts (or with a non-standard width of 4 to 7 pixels) or some motion graphics. Until the next chapter about HGFX, let's try to calculate how much effort these offsets will save us when putting sprites to positions other than multiples of eight.

In conclusion, searching readers can be hinted to some expert lessons: offsets can surprise us, eg they can hold negative numbers and maybe they will help us with HiRes graphics.
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

HGFX Intro, part 4

4. HGFX video memory location

The video memory of the HGFX system consists of 3 areas:

Code: Select all

   The HGFX VideoRAM of 6144 bytes.
   Configuration memory registers.
   Colour registers (index of 256 colours) with a size of 768 bytes.

HGFX VideoRAM, ConfigRAM and colourRAM can be located in the ZX Spectrum memory space from address 0 anywhere, at multiples of 256 bytes. It is configured using three HGFX ports.

Code: Select all

  g_zxi_021 high byte of VideoRAM area
  g_zxi_022 high byte of Registers area
  g_zxi_023 high byte of Indexed colour table area
When the HGFX is turned on, the beginning of a videoRAM is set to the original ZX-bitmap, at 16384 (40h * 100h = 4000h = 16384 dec). You can also page it into the ROM area (writes to HGFX memories work here as well).

Comments:
EleMeNt ZX/MB hardware installs new extensions on reserved ZXi ports. None of the HGFX ports will ever
collide with existing, correctly designed ZX-peripherals. HGFX ports and registers are listed in the Programmer's Reference Guide on 128land.com, in the Documents section.

The current version of HGFX has a total of 7 control ports and 7 memory registers, of which four registers are 16-bit.

OUT instructions (to the HGFX ports) are usually used only once, to turn system settings on (where is the VideoRAM, where are colours, and where are registers). Then you don't have to touch them anymore. Further control takes place by writing to the HGFX registers, which are stored in standard memory and can be handled, e.g. incremented, faster than ports.

Check the ProgRef manual how the HGFX memory registers work with two video buffers. When dealing with the planar you won't find any buffer in videoram, as buffers are an internal part of the HGFX system. Two internal, working screen buffers always store data of all bitplanes. They can be controlled through HGFX's registers perfectly, much better than we are used when switching between two classic ZX-screens.

5. HiRes - "fine" resolution of 512*192 pixels

Haven't I told you yet that the HGFX can do a high resolution graphics? With full colours and, unlike HRC (HiResColour) graphics modes, without any attribute compromises. Even in HiRes, just like in 256*192 resolution, each pixel is a different colour. HGFX is the first system which can display on the ZX Spectrum (with a little trick) in 6144 bytes all 256 colours in hires graphics, with 512 pixels per line.

One line (point 0 to 511) occupies 64 bytes in HiRes (in the original ZX Spectra resolution of 256*192 points, which now we can name "LowRes", whether it was non-linear or linear, it is always 32 bytes).

Code: Select all

    HGFX HiRes linear video memory

        1st line  ...  byte 0 to 63
        2nd line  ...  byte 64 to 127
        3rd line  ...  byte 128 to 191
        ...
Double resolution means the VideoRAM space of 6144 bytes is enough for half of the HiRes screen only. In order to controll the entire space at once, and always with the same simple way, we will help ourselves with an offset.

Code: Select all

---------------------------------------------------------------
| X,Y: 0,0                                         X,Y: 511,0 |  
| adr. 16384          512 pixels, 64 bytes         adr. 16447 |  
| Offset Y=0                    per line                      |
|                                                             |
...                                                         ...
|                 96 lines                                    |
| X,Y: 0,95         96*64 = 6144 bytes             X,Y: 511,95|  
| adr. 22464                                       adr. 22527 |  
| Offset Y=0                                                  |
---------------------------------------------------------------
| X,Y: 0,96                                        X,Y: 511,96|  
| adr. 16384          512 pixels, 64 bytes         adr. 16447 |  
| Offset Y=96                   per line                      |
|                                                             |
...                                                         ...
|                 96 lines                                    |
| X,Y: 0,191        96*64 = 6144 bytes             X,Y:511,191|
| adr. 22464                                       adr. 22527 |  
| Offset Y=96                                                 |
---------------------------------------------------------------

HiRes can also be combined with ZX screen and so on, for example. enrich the attribute graphics with a fine font...

6. Index colour format, border

A HGFX colour is made from a 24-bit (3 bytes) RGB value stored in memory so that the lowest byte, colour, is stored first folder (B)lue (similar to what the Z80 CPU does when it saves 16-bit numbers, eg number #4000 will be stored in the order #00, #40.

Code: Select all

Example:
         addr - blue value
         adr+1 - green value
         addr+2 - red value
We can also define it as the system BGR(0-255,0-255,0-255).

The original ZX Spectrum palette we could define in HGFX as follows:

Code: Select all

     Colour    Binary    B-G-R       Colour
     number    value     storage     name
    ----------------------------------------------------
       0        0000     #000000     black
       1        0001     #B60000     blue   (BRIGHT 0)
       2        0010     #0000B6     red
       3        0011     #B600B6     magenta
       4        0100     #00B600     green
       5        0101     #B6B600     cyan
       6        0110     #00B6B6     yellow 
       7        0111     #B6B6B6     white
       8        1000     #000000     black
       9        1001     #FF0000     blue   (BRIGHT 1)
      10        1010     #0000FF     red
      11        1011     #FF00FF     magenta
      12        1100     #00FF00     green
      13        1101     #FFFF00     cyan
      14        1110     #00FFFF     yellow 
      15        1111     #FFFFFF     white
This is how it is "poked" to the memory:

Code: Select all

    DATA 0,0,0     ; black
    DATA 182,0,0   ; blue (no bright)
    DATA 0,0,182   ; red (no bright)
    DATA 182...    ; magenta (no bright)
By default, port 254 is used to select the screen border colour from basic 8 colours (i.e. no bright). If a border-index-colour is turned on in the the HGFX, the port 254 specifies which colour stored in one of the first eight colour indexes will be the border colour.
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

HGFX Intro, part 5

In the fifth part of the introduction to HGFX, we will talk about two basic and often used system functions, combined with the value PlanarMask.

In the beginning let's repeat with an example to make sure we understand how to work with bitmaps, to become familiar with graphics layers. Controlling the HGFX is simple if you are experienced in bitplanes (mainly because of colours), other things to do are easy then.

Example: We need a three-colour sprite, an arrow. Because we write into bitplanes, we have graphic data prepared in layers. On the ZX Spectrum, there are mostly monochrome or two-colour sprites, i.e. 1 bitmap (1 layer, possibly with a mask), in which active bits specify the image and zeros mean an empty space where is nothing or a background is rendered. For our three colours we need 2 layers (they allow up to 4 colours). Folded layers will give us the desired result.

With the arrow, we intend that:

Code: Select all

    colour no.1    will colour    the shiny (top) half of the arrow
    colour no.2                   the matte (bottom) half of the arrow
    colour no.3                   edges and the spine of the arrow
In the rows, the colours are arranged as follows:

Code: Select all

            Colour 1    Colour 2     Colour 3    

    b0        1            0            1
    b1        0            1            1
By putting everything "on top of each other" in the planar, are bits, lying on top of each other in individual layers, connected and provide the expected colour. In our case, for example, colour No. 3 must have a bit set in each layer. Both layers and the displayed result look like this:

Code: Select all

   1. layer           2. layer            Result

   + + + + + + + +    + + + + + + + +     # # # # # # # #  
   + + + + + + + .    + + . . . . + .     # # @ @ @ @ # .  
   + . + + + + . .    + + + . . + . .     # % # @ @ # . .  
   + . . + + + + .    + + + + . . + .     # % % # @ @ # .  
   + . . . + + + +    + + + + + . . +     # % % % # @ @ #  
   + . + . . + + .    + + + + + + + .     # % # % % # # .  
   + + . + . + . .    + + . + + + . .     # # . # % # . .  
   + . . . + . . .    + . . . + . . .     # . . . # . . . 
We will place the arrow in bitplanes 3 and 4. That means the first arrow-graphics layer will go to bitplane 3 and the second layer to bitplane 4.

We will set the desired colour beforehand. Let's draw then:

1. PlanarMask value = 8 (000001000 bin, i.e. bitplane no.3)
2. We render the first layer of the sprite.
3. PlanarMask value = 16 (000010000 bin, i.e. bitplane no.4)
4. We render the second layer of the sprite.

Even though it would seem that re-printing sprite graphics layers into bitplanes (a kind of stamping across bitplanes) must slow down programs, it won't happen.

With the HGFX, we save time not only that we selectively work with bitplanes and graphics layers, and we also use offsets, so do not have to scroll graphics bit by bit. This is supported by simple but very fast special functions for copying and deleting.

As a result, even with 16 sprite colours, the HGFX is faster than original ZX Spectrum monochrome or attribute graphics.

How different graphics can be combined, in several layers and with a different number of colours, we will show in the example of a flying owl (famous animation from Agony demo). In the HGFX version, you can find it at at vimeo.com/429052052



Static, animated and scrolling graphics areas of this demo are divided between the bitplanes, with the ratio 1 : 3 : 2 : 2

Code: Select all

                Bitplanes:

                           /               
    grass                 /     no. 0      
   (nearest foreground)  ------------------
                          /     no. 1      
  ===================    ------------------
                          /     no. 2      
    trees (foreground)   ------------------
                          /     no. 3      
  ===================    ------------------
                          /     no. 4      
    logo                 ------------------
      and                 /     no. 5      
       flying owl        ------------------
                          /     no. 6      
  ===================    ------------------
    dark forest           /     no. 7      
      (background)       ------------------
Naturally, the owl, the main object on the screen requires the most colours. Its graphic data is copied into 3 bitplanes.

An interesting thing, apart from the planar layers, is that the farthest background with the moon and stars is not stored in any bitplane, but in the ZX-screen. ZX-screen "shines" through bitplanes where HGFX is colour #0. In addition to the 8 bitplanes, there is one more layer, the classic ZX with coloured attributes.


COPY_ALL and ERASE_ALL


are parts of a separate unit in HGFX. These are functions, which can do two useful actions: on command, they will perform either copying or deleting, within the entire buffer.

Both functions will start by a command, written to a memory HGFX register.

COPY_ALL always copies the entire content of the videoRAM from one buffer (source) to another (destination). While doing so, it is governed by value PlanarMask, which allows you to copy only some bits of each point, i.e. copy only some bitplans.

ERASE_ALL fills the entire buffer with zeros. The PlanarMask says, which bitplans are involved. So it erases, through the mask, chosen layers - bitplanes (when we have a value of 0 in PlanarMask, no bit mask is set, nothing is deleted).

Both functions process the entire videoRAM and that takes some time. During this period is not suitable to work with videoram, but the Z80-CPU can do other activity. To find out that a function has ended and we can reach again buffers, the STATUS memory registr is used.

For a quick idea, a matter of speed: the COPY operation takes less than two milliseconds, the ERASE a little more than 1 millisecond. For programmers: the COPY_ALL time is well remembered, 3.5 MHz machine implementation this function takes 6144 T-states :-)
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

HGFX Intro, part 6

Let's conclude beginner lessons about the HGFX. For the skilled part of the ZX community they were unconventional and lengthy as it went by a tortuous method from general to difficult. Now we end with two things in terms of HGFX very substantial that you will usually find in a technical documentation at leading places.

Write and read methods can be very creative in the ZX-planar. We already understand how to clear an entire byte or change only pixels whose value is set to 1. Now let us look at something more complicated: how to keep loaded content in an internal buffer in order to return it later, to another location. For this, we have different modes and working methods.

HGFX operating mode - Ink Mode

Previous descriptions considered this mode. Ink Mode means: where a logical one is in the written value, the bit from selected index colour will be written to the active videobuffer. Where there are logical zeros, there is nothing to change.

HGFX operating mode - Copy Mode

Copy Mode, more complicated than Ink Mode, can operate via two methods:

The first method is write-only. Where a bit 1 is written (set) to the video memory, the pixel is coloured as in the Ink Mode. Where is 0, everything is reset to zero. In other words, a writing puts (in combination with the PlanarMask) the colour with the index-number 0 to places where no colour is written.

The second method occurs automatically whenever a reading from videoram was previously done. In this case, a writing will handle a content which was used in the last reading. Watch out: The "read" value must match the subsequently written value. If both values do not match, only the first writing method of the Copy Mode is performed.

The following applies in the Copy Mode:

1. If a reading from videoram precedes a writing and "write" and "read" values are the same, colours are copied in the full range of 8 pixels.

2. If the value is read from the videoram first, the subject of a next "write" operation will be the content of the previous videoram "read" position.

It doesn't matter if 0 or 1 are written, 8 pixels are copied.

An example of the simplicity of this seemingly complex Copy Mode is a screen scrolling, as we know it from ZX Spectrum BASIC editor. When the ROM scrolls up, it reads a byte from the videoram - now, watch out, this is where the Copy Mode switches to the second method! - and writes it by 8 pixels higher. And round and round, if we comply with a read/write order.

Offsets are also applicable in the Copy mode, readings and writings have their own custom offsets. Each write or read operation can be done at another place, even from another buffer. It is possible to set from which and to which LowRes buffer we want to copy (note: in the case of HiRes, 512*192 resolution, we only have one buffer).

Chunky graphics

We can think of chunks as a format (arrangement) of data or as a display mode. It's a megablock of memory, arranged so that each pixel can be coloured with one of 256 colours and occupies the entire one byte. Chunky brings a problem of a huge video memory of 48 KB (by a resolution of 256*192 pixels). Older, Z80-type processor, without specialized DMAs or other logic chips has a lot of work to do in order to smoothly execute something in such a memory, e.g. scrolling spends all its machine time for byte transfers.

Code: Select all

  256 coloured pixel chunky graphics

        256*192 = 49152 pixels (48 KBytes)

    1st      2nd      3rd                   49152.
    pixel    pixel    pixel                 pixel
 |--------|--------|--------|...  ...  ...|-------|
                                                 
   byte 0   byte 1   byte 2              byte 49151
HGFX can expose its graphics data in addition to planar graphics also in this chunky format. Honestly: internal HGFX buffers are managed in chunky format and we can access them via planar or via chunky mode.

The planar is undoubtedly suitable for more modest computers (especially in eight-bit proportions), chunky graphics for more powerful hardware. Due to different accesses to video memory, some chunky effects are difficult to implement in planar (and vice versa).

In the HGFX, chunks can help with one basic thing, namely with a direct reading from buffers (that is not possible in planar). In the Ultimate memory mode (of the eLeMeNt ZX/MB hardware) we can view chunky areas through 8 KB or 16 KB windows. However, chunky memory is not paged! Fast eight-bit paging the registers of eLeMeNts or MB03s have no meaning for HGFX. We have to manage it in a special way: We have to set the offset Y for the beginning of the area we want to make available. For example, it is possible to have pixel data from line 120 at address 0.

Example of printing in Chunky mode: drawing a pixel at coordinates 128,96 in colour 158

Code: Select all

    RAM chunks set to eight KB space from 8192 to 16383
    OffsetY = 96
    POKE 8192+128,158
To make the chunky mode more usable, the system respects some HGFX planar settings. In addition to offsets, we will use the PlanarMask and HGFX adjustment bits of the register No. 0 (video parameters). The PlanarMask behaves, surprisingly, just like in planar :-) when you write a byte, it is processed through the PlanarMask, so even in the case of a chunky memory, only active bits are overwritten, other data (in place of zeros in PlanarMask) does not change, they will retain previous values.

Simply put: in planar, the bits are stacked on top of each other, in chunks they are lying next each other, but the mask always does the same when writing to the memory. However, the difference is there anyway, in favor of the planar: By writing one byte, in the planar we serve 8 (whole "chunky") pixels at the same time, but only one pixel in the chunky mode.

Another good thing that will make our work with the chunky graphics easier is an option to switch buffers and set different buffers for different ones operation. We can thus read and write from different buffers without any additional switchings.

Final part?

The basic series about HGFX ends. The next and last one will be about HAM, FILL, SuperHires modes.

I wish you a lot of fun whether you run the HGFX on real machines like eLeMeNt ZX (and the MB03+ Ultimate in the future) or you test the HGFX on the LnxSpectrum (which has a lot of support for the HGFX programming, eg it can import graphics into the HGFX bitplane format).

Thanks go to: Lanex, LMN128 and Hood, for a big help with this series. And also for the brotherhood, thanks to whom we could tune the ZX Spectrum for the 21st century. At the same time, it was possible to merge different approaches (clones, peripherals), western and eastern spectrist styles: do not exclude proven old things and combine more new and good things. You can find an overview of all developments of the past few years at www.128land.com

I also thank everyone who contributed with their hands and heads, for their support, they simply gave the new hardware of the eLeMeNt ZX/MB family a chance: pvym, Zoom, Busy, lordcoxis, nihirash, NEO SPECTRUMAN, Tchunass, Velesoft, Martin8bity, dakidski and others + applause goes to the esxDOS community, without them our attempt wouldn't be successful!
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

add-ons

Holding post agreed with admin
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

Simple HGFX demonstrations for download:

https://zxfiles.net/

Image

Web address changed by PJ at request of author - 27/12/22
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

Content:
part 2 - pixel drawing
part 3 - videoRAM format, transparency, offsets
part 4 - video memory, HiRes, colour indexes and palette
part 5 - sprites explanation, fast copy and erase
part 6 - INK and COPY modes, chunky

The part no. 6 will be delivered no sooner than next Sunday.

Agony demo technique, incl. ZX-screen mix in the background, will be briefly described in the part 5.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: HGFX - ZX Planar

Post by RMartins »

From all the documentation/explanation mentioned, this seems to be a new Graphics card interface, that behaves in a similar way to how the Commodore Amiga and similars worked.

I'm not sure, if all the extra data and code needed to make this work, will allow for fast updates, since we are adding a lot of data.
The duplication on the several bit planes done automaticaly by the card, only seems to be useful when in ZX compatibility mode.

One of the pains of working with an Amiga, was exactly having to handle the several bit planes.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: HGFX - ZX Planar

Post by RMartins »

SamC wrote: Fri Jul 22, 2022 7:29 pm
3. Offsets[/b]

...

We set OffsetX=0, OffsetY=16 and print a character to the videoram. We store the first byte at address 16384, the next byte at 16416 (16384+32), etc. A character will appear on the screen as printed using basic's PRINT AT 2.0;
The address 16416 is only correct if the offset was set to 32 and not 16.
Correct ?
AndyC
Dynamite Dan
Posts: 1362
Joined: Mon Nov 13, 2017 5:12 am

Re: HGFX - ZX Planar

Post by AndyC »

RMartins wrote: Thu Jul 28, 2022 12:40 am The duplication on the several bit planes done automaticaly by the card, only seems to be useful when in ZX compatibility mode.
I'd imagine it helps whenever you draw in a solid colour, because you can effectively set the colour and then just write as if it were 1bpp. Writing multicoloured objects would be trickier though, and that was where the Amiga started to fall down.
User avatar
PeterJ
Site Admin
Posts: 6823
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: HGFX - ZX Planar

Post by PeterJ »

I think @SamC is on vacation so there may be a delay in his reply.
User avatar
Joefish
Rick Dangerous
Posts: 2020
Joined: Tue Nov 14, 2017 10:26 am

Re: HGFX - ZX Planar

Post by Joefish »

I don't see how the Spectrum can write this amount of data at anywhere near the speed required for that animated demo. You'd be hard-pressed to make that parallax scrolling / owl animation work in monochrome at a smooth rate, never mind with eight times as much screen memory to update.

Bitplane graphics were fun to work with - I had an ST - and they certainly lend themselves well to parallax scrolling, if done right. Varying the number of bitplanes you could be bothered to write to was also a simple way of drawing graphics much faster.

But then out came PC graphics cards with byte-per-pixel display modes, which made it easier to move and scale pixel patterns; someone wrote a 3D textured game called Wolfenstein 3D, and that was pretty much the death of bitplane screens.
User avatar
Lethargeek
Manic Miner
Posts: 721
Joined: Wed Dec 11, 2019 6:47 am

Re: HGFX - ZX Planar

Post by Lethargeek »

Joefish wrote: Thu Jul 28, 2022 12:36 pm I don't see how the Spectrum can write this amount of data at anywhere near the speed required for that animated demo. You'd be hard-pressed to make that parallax scrolling / owl animation work in monochrome at a smooth rate, never mind with eight times as much screen memory to update.

Bitplane graphics were fun to work with - I had an ST - and they certainly lend themselves well to parallax scrolling, if done right. Varying the number of bitplanes you could be bothered to write to was also a simple way of drawing graphics much faster.

But then out came PC graphics cards with byte-per-pixel display modes, which made it easier to move and scale pixel patterns; someone wrote a 3D textured game called Wolfenstein 3D, and that was pretty much the death of bitplane screens.
AFAIU the amount of data to write is not the same as the amount of screen memory actually modified (same as with EGA planar modes on the PC). OTOH i think planar gfx makes little sense today with fast FPGAs and lots of cheap SDRAM available, it was only better for glue logic and early custom chips.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: HGFX - ZX Planar

Post by RMartins »

AndyC wrote: Thu Jul 28, 2022 8:41 am I'd imagine it helps whenever you draw in a solid colour, because you can effectively set the colour and then just write as if it were 1bpp. Writing multicoloured objects would be trickier though, and that was where the Amiga started to fall down.
Yes, It will, but then what is the reason of having such an interface ?
If not to have every single pixel independently colored ?
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

Hi,
it seems that a planar system with 8 bitplanes must be 8x slower. However, that is not true. You don't need to 1) recount bitmap address 2) roll data (shift bits) 3) mask screen data 4) handle all bitplanes at once.
Therefore is planar more effective for slower computers than a planar/chunky system. Although the HGFX does not use blitter, it can provide some automated tasks.
Please wait for next parts of this tutorial, where copy mode and simple fast functions will be described.
AndyC
Dynamite Dan
Posts: 1362
Joined: Mon Nov 13, 2017 5:12 am

Re: HGFX - ZX Planar

Post by AndyC »

RMartins wrote: Fri Jul 29, 2022 12:57 am Yes, It will, but then what is the reason of having such an interface ?
If not to have every single pixel independently colored ?
Well it'd be great for drawing flat filled polygons and such, since you'd only need to draw them in 1bpp.

There's also the trick of "colour plane sprites". So if you reserve a couple of bitplanes you can use them exclusively for drawing sprites etc. Then you set the palette entries such that multiple palette entries share the same output colour, which creates the effect of looking like things move in front of behind of things without erasing the background.

I'm still curious about the idea, even though I'm not sure there is ever likely to be much demand for such a thing (even less so with the Next existing as a solution) as it's an interesting "what could have been" scenario.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: HGFX - ZX Planar

Post by RMartins »

I'm wondering, if this is just a thing for these FPGA boards ?
Which it seems to be.

Could be interesting to be implement on ZX Next, but I think the real killer application for this (if it is really that fast or simple to use, although it doesn't quite seem to be), would be and Add-On board to plug in on a regular Spectrum Expansion interface.

Although I do find it interesting that the ElementZX Board, has a real Z80, and it can be upgraded to the maximum available manufactured Z80 speed, of 20MHZ!
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

RMartins wrote: Thu Jul 28, 2022 12:55 am The address 16416 is only correct if the offset... Correct ?
No. The offset 16 means an Y-coordinate and is not dependent on physical memory.
Addresses and offsets are different systems. The videoram addresses and BASIC PRINT examples are logically applied only when offsets are set to (0,0).

Offsets are more identical to pixel coordinates.
Offsets moved a screen window, their coordinates means a new upper left corner of the screen (started at the HGFX videoRAM address).

Using offsets is a bit like drawing. You change offsets only and store a pixel value always to the same place of memory, with one POKE command.
Offsets is the place, where the POKE start-of-videoRAM, value is done.
POKEing to all other areas of the video memory is possible, but actually not necessary. It's there for zx-compatibility. :-)

Please also note that offset means all screen pixels are actually shifted (rolled) over bytes of videoRAM.
OffsetX = 255 and OffsetY = 191 and POKE 16384,255 will light up the last dot in the lower right screen corner.

We no longer need to recalculate bits (pixels) or move them between bytes. E.g. printing a character with a width of 5 pixels... we don't need to search for an exact pixel position or move bytes of character templates. A print position is done by OffsetX = 0, ... then 5, 10, 15 etc.
We take the first byte from the character template and use POKE. The system does all writings and calculations for us.

I don't think I can explain it better. :?
The good news is that the author of the HGFX is creating a more practical instruction with examples:
https://wiki.ilnx.cz/doku.php/hgfx:start

Anyway, I will continue the introductory series tomorrow.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: HGFX - ZX Planar

Post by RMartins »

It might be me, but it seems that there is a bit of a mix or confusion between both statements.
SamC wrote: We set OffsetX=0, OffsetY=16 and print a character to the videoram. We store the first byte at address 16384, the next byte at 16416 (16384+32), etc. A character will appear on the screen as printed using basic's PRINT AT 2.0;
This first one, seems to imply that offsets are character based, but then the math doesn't add up.

Every time we write, we only write a byte. Even if the system is looking at the offsets as pixels.
So writing several bytes (typically 8 lines) to make up a character, requires several writes.

So either we are forced to setup the offsets between each byte write, or there is some other "stride" setting, that allows the system to know that next write will be updated with a specific "stride" or distance between addresses.
SamC wrote: Sat Jul 30, 2022 6:04 pm No. The offset 16 means an Y-coordinate and is not dependent on physical memory.
Addresses and offsets are different systems. The videoram addresses and BASIC PRINT examples are logically applied only when offsets are set to (0,0).

Offsets are more identical to pixel coordinates.
Offsets moved a screen window, their coordinates means a new upper left corner of the screen (started at the HGFX videoRAM address).
OK, the problem is that on your initial message (see quote above) your offset was 16 and not 32 !

So how the second write results in 16416 (16384+32) ?
That was the question.

NOTE: Assuming we are in a 256 pixels width mode, where 32 bytes per pixel line are used.

SamC wrote: Sat Jul 30, 2022 6:04 pm Using offsets is a bit like drawing. You change offsets only and store a pixel value always to the same place of memory, with one POKE command.
Offsets is the place, where the POKE start-of-videoRAM, value is done.
POKEing to all other areas of the video memory is possible, but actually not necessary. It's there for zx-compatibility. :-)

Please also note that offset means all screen pixels are actually shifted (rolled) over bytes of videoRAM.
OffsetX = 255 and OffsetY = 191 and POKE 16384,255 will light up the last dot in the lower right screen corner.
OK, so following your reasoning here, offsets are defined has a pixel coord and when you poke it, you provide, not the bits to set several pixels, but the intended pixel color (palette Index) ?

Which implies that we require 64 poke (OUT instructions) to draw an 8x8 character ?

SamC wrote: Sat Jul 30, 2022 6:04 pm We no longer need to recalculate bits (pixels) or move them between bytes. E.g. printing a character with a width of 5 pixels... we don't need to search for an exact pixel position or move bytes of character templates. A print position is done by OffsetX = 0, ... then 5, 10, 15 etc.
We take the first byte from the character template and use POKE. The system does all writings and calculations for us.
OK
But does the system assume, that if we do another poke, that the X or Y offset implicitly incremented ?
Can we choose if in X or Y ? or is it fixed ?

Would make sence to improve output using the asm instructions (OUTI or OUTIR)

Or it doesn't auto increment and we need to set another Offset by hand for the next pixel ?

Maybe explaining how we would write a full 8x8 character or sprite, i.e. 64 pixels (preferably in ASM) at a specific position on screen, in multi color, would be ideal.

Or is this just for BASIC interface ? (Using the term POKEs, kinda leeds in that direction)
SamC
Microbot
Posts: 163
Joined: Sun Sep 29, 2019 9:07 pm

Re: HGFX - ZX Planar

Post by SamC »

Hi all,

sorry for the longer delay, due to personal reasons I haven't had more time to work on several things related to old computers...

I will answer questions, but first let's wait for the second half of the series (parts 4, 5 and 6), which Peter will publish here later (Tuesday?).

In the meantime, I am working on an additional part for beginners, about the remaining possibilities of HGFX, displaying graphics in HAM, FILL and SuperHiRes modes.

For those interested in programming, I have news that the author of the HGFX system himself has started writing assembler examples.

Another good news is that the assembler in the LnxSpectrum emulator has a betaversion of BPL command, which can include (load and convert) a common PC graphics into the HGFX bitplane format, cleverly, in different ways, with many parameters.

Image

SamC.
User avatar
PeterJ
Site Admin
Posts: 6823
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: HGFX - ZX Planar

Post by PeterJ »

SamC wrote: Sun Dec 25, 2022 8:43 pm I will answer questions, but first let's wait for the second half of the series (parts 4, 5 and 6), which Peter will publish here later (Tuesday?).
Holding posts have been updated on page one of this thread.
User avatar
Luzie
Manic Miner
Posts: 905
Joined: Fri May 01, 2020 2:07 pm

Re: HGFX - ZX Planar

Post by Luzie »

May I ask something not directly related with HGFX ... Can someone recommend me a Freeware Graphics Editor for Windows which supports 256 Colour depth including Palette File (*.pal) Support?
Post Reply