ruby-Z80

Gemfile

gem 'z80', git: 'https://github.com/royaltm/z80-rb.git'

A Z80 assembler powered by Ruby.

Ruby is a powerful meta-language, so why not leverage its meta powers to have the ultimate macro system, compiler, and builder for the Z80 assembler?

Now, to answer the question, you'll need:

then

gem install specific_install
gem specific_install royaltm/z80-rb

go to Ruby's irb or your REP of choice and snap'&'paste this:

require 'z80'

class MyZXMath
  module Macros
    def mul8(eh, el, th, tl)  # performs hl * a using (a, th, tl)
                              # stops on CARRY out
                              # result to (eh|el)
      raise ArgumentError unless th|tl != hl
      ns do |eoc|
                ld  tl, l
                ld  th, h
                ld  hl,0
        loop1   srl a
                jr  NC, noadd
                add hl, th|tl
                jr  C, eoc
        noadd   jr  Z, ok
                sla tl
                rl  th
                jp  NC, loop1
        ok      label
                unless  eh == h and el == l
                  ld  el, l
                  ld  eh, h
                end
      end
    end
  end

  include Z80

  export mul
  mul   mul8(b, c, d, e)
        ret NC
        rst 0x08             # ERROR-1
        data  1, [0x0A]      # Error Report: Integer out of range
end

class Program
  include Z80
  include Z80::TAP

                ld   hl, [multiplicand]
                ld   a,  [multiplier]
                jp   math.mul

                org 0x0020
  multiplicand  words 1
  multiplier    bytes 1

  import MyZXMath, :math

end

calc = Program.new 0x8000

puts calc.debug

check the debug output:

8000: 2A0980      ld   hl, (8009H)     -> multiplicand
8003: 3A0B80      ld   a, (800bH)      -> multiplier
8006: C30C80      jp   800cH           -> math.mul
8009: 00 00                   ..       :multiplicand
800B: 00                      .        :multiplier
800C:                                  :math
============== MyZXMath ==============
800C:             --- begin ---        :mul
800C: 5D          ld   e, l
800D: 54          ld   d, h
800E: 210000      ld   hl, 0000H
8011: CB3F        srl  a               :mul.loop1
8013: 3003        jr   NC, 8018H       -> noadd
8015: 19          add  hl, de
8016: 380B        jr   C, 8023H        -> EOC
8018: 2807        jr   Z, 8021H        :mul.noadd -> ok
801A: CB23        sla  e
801C: CB12        rl   d
801E: D21180      jp   NC, 8011H       -> loop1
8021:                                  :mul.ok
8021: 4D          ld   c, l
8022: 44          ld   b, h
8023:             ---  end  ---        :mul.EOC
8023: D0          ret  NC
8024: CF          rst  08H
8025: 0A                      .
^^^^^^^^^^^^^^ MyZXMath ^^^^^^^^^^^^^^

wait, there's more…

require 'zxlib/basic'

prog = ZXLib::Basic.parse_source <<-END
  10 CLEAR #{calc.org-1}
  20 LOAD ""CODE
  30 INPUT "Multiplicand: ",x
  40 INPUT "Multiplier: ",y
  50 POKE #{calc[:multiplicand]},x-INT (x/256)*256
  60 POKE #{calc[:multiplicand]+1},INT (x/256)
  70 POKE #{calc[:multiplier]},y
  80 PRINT "x: ", x, "y: ", y
  90 PRINT USR #{calc.org}
 100 GO TO 30
END

puts prog

Let's make a .tap file now:

prog.save_tap 'calculator', line: 10
calc.save_tap 'calculator', append: true, name: 'multiply'

go to ZX Spectrum or an emulator:

LOAD "calculator"

and load the calculator.tap file.

Enjoy!

Examples

Sources for the examples can be found in the example directory.

Click on an image to run the example in a web emulator:

The YARTZ demo released at Speccy.pl/2019 was made entirely using this gem, including music.

Bootstrap

You may use the zxinit tool provided by this gem to bootstrap a new program.

Provide a target file name and optionally the main class name.

$ zxinit hello_world
ZXINIT: initializing program HelloWorld at ./hello_world.rb
ZXINIT: ready
ZXINIT: compile and run HelloWorld with:

  zxrun "./hello_world.rb" "hello_world.tap"

Another tool: zxrun can be used to optionally compile ruby sources and run the emulator with the last argument provided.

$ zxrun hello_world.rb hello_world.tap
8000:                                  :start_test
8000: D9          exx
8001: E5          push hl
8002: CD0880      call 8008H           -> start
8005: E1          pop  hl
8006: D9          exx
8007: C9          ret
============= HelloWorld =============
8008:                                  :start
8008: 3E02        ld   a, 02H
800A: CD0116      call 1601H           -> rom.chan_open
800D:             --- begin ---
800D: 111880      ld   de, 8018H       -> text_data
8010: 010D00      ld   bc, 000dH       -> (+text_data)
8013: CD3C20      call 203cH           -> rom.pr_string
8016: 180D        jr   8025H           -> EOC
8018: 48 65 6C 6C 6F 20 77 6F Hello wo :start.800d.text_data
8020: 72 6C 64 21 0D          rld!.    :start.800d.text_data
8025:             ---  end  ---        :start.800d.EOC
8025: C9          ret
^^^^^^^^^^^^^ HelloWorld ^^^^^^^^^^^^^
Program: "hello_worl" LINE 9999 (58/58)
Bytes: "hello_worl" CODE 32768,38

DSL API

Z80 libraries

ZX Spectrum libraries

ZX Spectrum utilities

Features:

bin/zxgallery
ZXGALLERY 0.4: Creates a TAP file with a ZX Spectrum screen gallery.
Usage:
zxgallery [options] screen_files...

  screen_files: paths to SCR files to be appended to the tape;
  options:
    -o, --output: the target file name (the .tap extension is optional),
    -c, --code: the address of the code in the range: 32768..51054
Example gallery

See ZXUtils::Gallery for more information.

bin/zxconv

(requires RMagick rmagick.rubyforge.org/)

ZXCONV 0.5: Converting images to ZX Spectrum is fun!
Usage:
zxconv source destination [options]
  rendering options:
  -m, --mode 0|1|2|3|4        color mode
          0: 15 colors
          1: 8  basic colors
          2: 8  bright colors
          3: 15 colors, bright colors have priority
          4: 15 colors, basic colors only on black backgrounds
  -h, --hires n|c|p|i         high resolution mode
          n: 256x192 pixels 8x8 color attributes (ZX Spectrum)
          c: 256x192 pixels 8x1 color attributes (ULA+)
          p: 512x192 pixels monochrome (ULA+)
          i: 256x384 pixels interlaced (ZX Spectrum 128k/ULA+)
  -d, --dither n|r|f[n|r|f]   dithering mode phase1,phase2
          n: none
          r: riemersma
          f: floyd-steinberg
  -c, --colors CCC....        list of allowed color indexes (0..7)
  -0..15, --bg N              background color (0..15)
  -r, --ratio N/N             bright/basic color level ratio
  -l|L, --[no-]autolevel      apply auto level to source image
  -g|G, --[no-]autogamma      apply auto gamma to source image

  destination format and content:
  -f, --format t|b|r|a        zx spectrum data file format
          t: save as TAP; one file is created
          b: save as binary data; separate files for scr and bitamp
          r: save as ruby source
          a: save as assembler source
  -s|S, --[no-]savescr        save ZX Spectrum screen data
  -b|B, --[no-]savebin        save pixel bitmap (linear) data
  -a|A, --[no-]saveattr       save color attributes (linear) data
  -i|I, --[no-]saveimg        save image file
                              (format determined by destination ext.)
  -x|X, --[no-]x2-pixels      enlarge and normalize output image pixels
                              (only applied for image file)
  default options are:
  -m0 -hn -dn -r4/3 -0 -ft -s -i

zxconv -m4 -l -x examples/horse.jpg horse.png

ULA+ modes are also supported:

zxconv -m4 -l -hc -x examples/horse.jpg horse_hicolor.png

zxconv -l -df -hp -x examples/horse.jpg horse_hires.png

Requirements

Ruby 2.1.0 or later.

If you want to use Bundler, then Ruby 2.3.0 will be required. The recommended Ruby version is 2.6.0.

Author

Rafał Michalski

Licensing

This package is free to use in open source under the terms of the Parity Public License.