Home
Admin | Edit

Armada - RISC OS 256 bytes

download youtube

A 256 bytes real-time intro for RISC OS 3.5+ (RISC PC+ / RPI, 640x512, 32 bpp), released at NOVA 2026.

It shows running XOR patterns behind a tumultuous "fog" that come and go.

The "fog" effect is powered by drifting HAKMEM 149 patterns plus a separate "blur" pass, the HAKMEM pattern is then XOR shaded.

This started from an early 2025 p5js accidental sketch where i was trying to do nice noise with the HAKMEM 149 algorithm, i decided to port this effect on RISC PC after X-87 to support some RO stuff at NOVA.

The port took few hours, it was close to 256 bytes without much effort, only had to fix top / bottom boundary artifacts and optimizing the blur pass / looking at the RISC OS API to replace some costly calls. (e.g. OS_ReadVduVariables by OS_ReadDynamicArea)

The combination of the cheap post processing pass and the unstable patterns + XOR shading adds the illusion of a depth effect.

The API dive was interesting as i didn't know that some of the mode flags can setup a greyscale palette (only RO 5.21+ though) or do some transforms (rotations), many bytes could be saved by going for 256 colors (with greyscale flag) but i prefered older RO compatibility so...

Assembled on RISC PC (RPCEmu) with a custom ARMv2 Forth / assembler, kinda served as a test bed for the tool as well.

ARM code

\ ARMADA \ 256b RISC OS intro
\ ---------> by grz / The Orz
: OS_Byte $6 ; immediate
: OS_ReadMonotonicTime $42 ; immediate
: OS_ScreenMode $65 ; immediate
: OS_ReadDynamicArea $5c ; immediate
: OS_RemoveCursors $36 ; immediate
: OS_ReadEscapeState $2c ; immediate
: OS_Exit $11 ; immediate

variable mode_block
variable l1
variable l2

create ARMADA_ARMv2_CODE
  $0 imm r0 mov
  mode_block r1 adr
  OS_ScreenMode swi

  $2 imm r0 mov
  OS_ReadDynamicArea swi
  r0 r14 mov

  OS_RemoveCursors swi

  \ compute offset
  $a0000 imm r11 mov
  $500 imm r11 r11 add

  l1 ->
    $13 imm r0 mov
    OS_Byte swi \ vsync

    OS_ReadMonotonicTime swi

    $10000 imm r1 mov
    l2 ->
      \ x
      $16 asr r2 r8 mov
      $2 lsl r8 r8 r4 add
      \ y
      $17 asr r3 r5 mov
      $2 lsl r5 r5 r12 add

      \ i
      $9 lsl r12 r11 r7 add
      $1 asr r4 r7 r7 add

      \ osc
      $80 imm r1 r8 and
      r7 r8 r8 add
      [] r8 r14 r6 ldrb
      $15 lsl r6 r2 r2 sub
      $2 asr r3 r2 r2 sub
      $1 asr r2 r3 r3 add
      $2 asr r3 r2 r2 sub

      $14 lsl r0 r2 r2 sub \ animate

      \ color
      r3 r2 r8 eor
      $18 lsr r8 r8 mov
      $150 imm r8 r8 rsb

      \ clamp
      $ff imm r8 cmp
      $ff imm r8 movgt

      \ fix top + bottom artifacts due to "blur" pass
      $fe imm r5 r9 adds
      $1fc imm r9 cmp

      $8 lsl r8 r8 r8 orr
      $10 lsl r8 r8 r8 orr
      [] r7 r14 r8 strls

      $1 imm r1 r1 subs
      l2 <- bne

    \ "blur" pass
    $a00 imm r14 r1 add
    $140000 imm r14 r10 add
    $a00 imm r10 r10 sub
    l2 ->
      [] $4 negate imm r1 r6 ldrb
      [] $4 imm r1 r7 ldrb
      [] $a00 imm r1 r8 ldrb
      [] $a00 negate imm r1 r9 ldrb

      r7 r6 r6 add
      r8 r6 r6 add
      r9 r6 r6 add
      $2 asr r6 r6 mov
      $8 lsl r6 r6 r6 orr
      $10 lsl r6 r6 r6 orr

      $4 imm [] r1 r6 str

      r10 r1 cmp
      l2 <- bmi

    OS_ReadEscapeState swi
    OS_Exit swics
    l1 <- b

\  screen_mode --> \ X640 Y480 C16M F60 (mode string is shorter than mode block but not compatible with old RO)
\    $58 c, $36 c, $34 c, $30 c, $20 c,
\    $59 c, $34 c, $38 c, $30 c, $20 c,
\    $43 c, $31 c, $36 c, $4d c, $20 c,
\    $46 c, $36 c, $30 c, $0 c,
  mode_block -->
    $1 l,
    $280 l,      \ 640
    $200 l,      \ 512
    $5 l,        \ C16M
    $1 negate l, \ default refresh rate
    $1 negate l,

ARMADA_ARMv2_CODE here $ff8 dump

p5js code

function setup() {
  createCanvas(640, 512)
  background(0)
}

let x = 0
let y = 0

function draw() {
  loadPixels()
  for (let l = 0; l < 16384*4; l += 1) {
    let i = (0xa0500 + ((((y >> 23) * 5) << 9) + (((x >>> 22) * 5) >>> 1))) & ~3

    x -= pixels[i + 0 + (l & 128)] << 21
    x -= y >> 2
    y += x >> 1
    x -= y >> 2
    x -= frameCount << 22

    let c = 336 - ((x ^ y) >>> 24) // can also remove the sub for a darker / busier version
    pixels[i + 0] = pixels[i + 1] = pixels[i + 2] = c
  }

  for (let i = 0; i < width * height * 4; i += 4) {
    let c = pixels[i + 4] + pixels[i - 4] + pixels[i + width * 4] + pixels[i - width * 4] >> 2
    pixels[i + 0] = pixels[i + 1] = pixels[i + 2] = c // + 4 on index is cool
  }
  updatePixels()
}

back to topLicence Creative Commons