Module

Puregrain.Machine.ZxSpectrum

Package
purescript-puregrain
Repository
roman-mkh/puregrain

The ZX Spectrum's screen: 8 × 8 cells, each with two colors, an ink and a paper, from the machine's 8 colors, plus one BRIGHT bit for both. With Puregrain.Cells, that gives images the Spectrum could show:

import Puregrain.Cells (ditherCells, nearestInCell, orderedInCell)
import Puregrain.Machine.ZxSpectrum as Zx

-- ordered dithering inside the cells, with a threshold map m
{ cells, image } = ditherCells noDiffusion (orderedInCell m) Zx.screen picture

-- error diffusion inside the cells
{ cells, image } = ditherCells atkinson nearestInCell Zx.screen picture

Ordered dithering, with the 8 × 8 Bayer map, looks most like the Spectrum's own art and keeps every tone; with error diffusion, Atkinson looks best.

cells holds each cell's Attribute: its ink, paper and BRIGHT, as a screen file needs them; toScr turns the whole result into one. The colors are those of Puregrain.Palette.Presets.zxSpectrum. The colors, the attributes and the file layouts in full: docs/machine-screens/zx-spectrum.md.

#Attribute Source

newtype Attribute

One cell's attribute: its ink and paper, as ZX color codes (0 black, 1 blue, 2 red, 3 magenta, 4 green, 5 cyan, 6 yellow, 7 white), and whether BRIGHT is set. Only the 71 values in attributes exist.

Instances

#ink Source

ink :: Attribute -> Int

The ink's color code, 0 to 7.

#paper Source

paper :: Attribute -> Int

The paper's color code, 0 to 7.

#bright Source

bright :: Attribute -> Boolean

Whether BRIGHT is set, for both ink and paper.

#attributes Source

attributes :: NonEmptyArray Attribute

Every attribute that looks different: per brightness, 8 cells of a single color and 28 pairs, 72 in all, minus one, because all black looks the same with and without BRIGHT (it's listed once, without). Ink is the darker color, the lower code, and paper the lighter, as in the Spectrum's default of black ink on white paper; a single-color cell has ink = paper.

They're listed in the order Puregrain.Cells tries them, where the earlier wins a tie: the single colors first, then the pairs by the distance between their two colors, nearest first (at equal distances, normal before bright, then by ink, then by paper). A cell that several palettes fit equally well thus gets the narrowest, the calmest.

#attributePalette Source

attributePalette :: Attribute -> CellPalette

The attribute's colors, for Puregrain.Cells: the ink, then the paper, or the one color of a single-color cell. With orderedInCell, the order means that a cell is ink with paper dots added as a pixel nears the paper.

#screen Source

screen :: CellMode Attribute

The Spectrum's screen mode, for Puregrain.Cells: cells of 8 × 8 pixels, each using one of the attributes, tried in their order, with attributePalette's colors.

#toScr Source

toScr :: { cells :: Array (Array Attribute), image :: Array (Array RGB) } -> Either String (Array Int)

A dithered screen as a .scr file: the 6912 bytes of the Spectrum's screen memory, as SAVE "name" SCREEN$ saves them, and as many emulators and tools open them.

  • { cells, image }: what Puregrain.Cells.ditherCells returns with this module's screen, for a 256 × 192 picture.
bytes = toScr (ditherCells noDiffusion (orderedInCell m) screen picture)

The first 6144 bytes are the bitmap, one bit per pixel (1 for the ink, 0 for the paper, the leftmost pixel in the highest bit), its rows in the Spectrum's interleaved order. Then come 768 attribute bytes, one per cell, row by row: BRIGHT × 64 + paper × 8 + ink, with FLASH off. A single-color cell has all its bits 0.

Left, with a message, for an image that isn't 256 × 192, for cells that don't form the matching 32 × 24 grid, or for a pixel that isn't one of its cell's colors: then the cells weren't chosen for this image.

#toTap Source

toTap :: String -> { cells :: Array (Array Attribute), image :: Array (Array RGB) } -> Either String (Array Int)

A dithered screen as a .tap tape image: a short BASIC program that starts by itself and loads the screen, then the screen as a CODE file. Open it in an emulator, or type LOAD "" on a Spectrum: the picture appears and stays until a key is pressed.

  • String: the name on the tape, up to 10 printable ASCII characters.
  • { cells, image }: as for toScr.
bytes = toTap "sphere" (ditherCells noDiffusion (orderedInCell m) screen picture)

Left, with a message, for a name that's too long or holds other characters, and as toScr for the screen. The tape format: https://worldofspectrum.net/zx-modules/fileformats/tapformat.html