Although it is widely known that the ZX Spectrum ULA answers to any request to an even I/O address, all documentation state that the official port number to use should be $xxFE (where xx is any 8 bit number).

I'm now updating my ULA core to add the Timex MMU memory manager, and this MMU uses I/O port $F4, which would conflict with the current implemented ULA. So I'm thinking about performing a more restrictive decoding so that the ULA reacts only to I/O port $FE, but I'm concerned about the possible consequences of this in existing games and applications.


  • Do any of you know of a game, demoscene app, or actually, anything, that uses another even port than $FE to access the ULA?
  • Alternatively, is there any ZX Spectrum emulator which I could parametrize/customize in a way that I can run it from a shell script to execute a list of game snapshots, and log the first time it accesses an even addressed I/O port other than $FE?
  • I think some of the U.S. Gold games (Beachhead?) used port 80h - But too long ago, memory too lousy...
    – tofro
    Commented Dec 2, 2016 at 18:54

4 Answers 4


There are games that, through design or mischance, access the ULA using IN 0 -- this tends to cause a crash when run on machines with an Interface 1. For example, there's a bug in Jet Set Willy when it leaves the 'paused' state:

        LD      HL,9A00h        ;Restore lowerscreen attributes
        LD      DE,5A00h
        LD      BC,100h
        LDIR                    ;After LDIR, C = 0
        LD      A,(80DEh)       ;Restore border
        OUT     (0FEH),A
L8B17:                          ;Entry point if game was not paused
        LD      A,(95D1h)       ;(C will equal 0FEh if entered here)
        CP      0FFH
        JP      Z,L8C01         ;Death?
        LD      B,0BFH
        LD      HL,85E2h
        IN      A,(C)           ;Oops!

I know of an application that uses a different port from #FE to change border colour. It concerns all of my border art viewers, and the reason why the different port had to be used is as follows.

I wanted to be able to use all 8 border colours. To get the best possible resolution, I could not re-load register values; all current registers had to be used. We have 7 registers for 8 colours; we output them using out (c),r. The remaining 8th colour is outputted using an undocumented command out (c),0. However, this causes an issue. The yellow colour has to be outputted using the command out (c),c, and if c was to be equal to #FE, then out (c),c would result in a high state of the beeper output and out (c),0 in a low state. Beeper noises are going to be produced. To avoid these noises, I reset two bits in the value in register c, to ensure that beeper and tape state stays at 0. Hence, instead of writing into port #FE, I wrote to the port %11100110=0xE6.

  • 0xE6 actually, I guess ;) Commented May 7, 2017 at 21:58
  • Yes, of course, you are right; I fixed the post.
    – introspec
    Commented May 7, 2017 at 22:10
  • This is quite a clever technique - no modern compiler could match that to be sure. Welcome to Retrocomputing Stack Exchange.
    – wizzwizz4
    Commented May 8, 2017 at 17:42

The Timex ULA is not the same as the original ULA and fully decodes all 8 bits of the low byte - hence it will respond only to $xxFE and not to $xxF4. If you're going to emulate the Timex, you probably need to do it properly rather than bodging it :-)

  • Currently, the implementation makes the ULA to answer to any even port except $F4, but as the Timex was intended to be Spectrum compatible, I was wondering whether there was any program that for some odd reasons, chose to use port $F4 to access the ULA. Commented May 10, 2016 at 10:17
  • 2
    The Timex isn't 100% compatible. This is one of those ways :-) Commented May 10, 2016 at 10:19
  • Even so, I´d like to know if there was any published program that triggered that particular incompatibility Commented May 10, 2016 at 10:21
  • As a former owner of a Timex TC2048, I can personally say there were several games that triggered incompatibilities. I can only remember Comando though; no idea which bug it was. Commented Jan 23, 2018 at 16:54

I have found a technique used by demosceners that relies on accessing the ULA from a I/O port other than $FE.

This technique is used to get full screen (including borders) graphics and animations. The technique works as follows: let's say you have graphics shown on the screen, the regular way, and you want to superimpose a scrolling text that goes from right to left, from the very right edge of the screen to the very left edge, that is, the text is shown sometimes over the border area, sometimes over the paper area.

Usually, controlled changes in the border colour are accomplished by writing to port $FE at a very precise moment, and controlled changes in paper colour by writing a new attribute value to memory.

But having a text that scrolls over border and paper needs a way to do this as fastest as possible, so the code shouldn't have to test if the ULA is painting the border or the paper, so the code switches to writing to port $FE or writing to memory. That's too many wasted cycles.

Instead, this technique allows for changes to the screen regardless of whether border or paper is being scanned.

The trick uses the two screen buffers present on 128K models. The main screen with the graphic, and the shadow screen, which presents a solid colour filling the entire paper area.

The scrolling text is written by writing to an I/O port at very precise times. For that, a port that triggers both the ULA and the paging hardware is used, such as $7FFC. Writing to this port writes to both the ULA and the 128K paging hardware.

The value written to this port is 0001agrb where:

  • a is 0 if we want to show the background image or background border, and 1 if we want to show the scrolled text.
  • rgb is the RGB code of the colour we want to show on the border: 000 is used for the background border and any other value corresponding to the colour of the scrolled text. For example, for a white text, it would be 111. The shadow screen is filled with this same colour.

To quickly change from background to scrolled text, the value 00011grb is stored in a register, and it is xored to the accumulator.

So, to start drawing the text, 00011grb is written to port $7FFC. If the ULA is scanning the paper area, this causes the shadow screen to be displayed, and a certain RAM bank to be paged in area $C000-$FFFF (which we won't care about). If the ULA is scanning the border area, this causes the border to be of this colour. The shadow screen will be selected as well, but as the ULA is not drawing the paper area, it won't matter.

To stop drawing text and revert to the background image, 00001000 is written to port $7FFC, which causes the regular screen buffer to be displayed (if the ULA is scanning the paper area) and the border to be black (if it is scanning the border area).

This technique can be observed in the small demo Scroll 2017: Scrolling Text GIF

  • can you share the demo? Standard ula128 does't have the scroll.
    – aholub7x
    Commented Feb 21, 2022 at 14:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .