Lace Mode

Scene+ #26

Lace Mode

Many of you have certainly seen some demos or games that use the so-called interlace mode to achieve more colors, shades, or—when vertical scrolling is used—doubled vertical resolution. This technique is also used by the Amiga hardware, for example, doubling the resolution there as well. The downside, of course, is the flickering, which depends on the colors used. The darker the colors, the less noticeable the flicker, just as when they are close to each other, e.g. magenta and green. Jaxon Hollis once wrote a program titled INT. XE (or something like that) with which you can create such pictures on the Spectrum. Unfortunately, I don't have this program and have never seen it, but I also like the idea of coding a text viewer or similar in lace mode to make the letters more readable. Sadly, the text viewer would not only be slightly slower, but would also have the disadvantage that it wouldn't work on a Spectrum 48, because interlace uses the second Spectrum screen—only that way is it possible to switch between screens fast enough.

There are two types of interlace: True interlace like on the Amiga—the Spectrum can only use this mode when the screen is scrolling, because there is no register to make the electron beam work in an interlaced scan so that on the second pass the beam draws anything exactly between the lines of the previous pass. And then there is a simple LACE where the colors of both screens are blended together (also works on the SAM!). On the Specci, this allows you to simulate an intermediate color shade in every ATTR pixel, if you want to. This LACE provides an absolutely steady image when both screen contents are identical.

In BASIC, you would switch between the screens like this:

10 LET Bank=16 : REM 16 = original bank
20 POKE 23388,Bank: OUT 32765,Bank : REM Switch
90 LET Bank=PEEK 23388
91 IF Bank>23 THEN LET Bank=Bank-8: GO TO 93
92 IF Bank<24 THEN LET Bank=Bank+8
93 POKE 23388,Bank
94 OUT 32765,Bank

The second screen is located in Bank 23, so you should switch to this bank if you want to draw anything there. The only problem is that it is impossible to use BASIC commands like PLOT, DRAW, CIRCLE, PRINT, etc. directly, since they were written for the normal screen starting at 16384, but Screen 2 is located 32768 memory addresses higher, starting at address 49152. With a small trick, however, it is still not impossible to draw there. You simply draw on the normal screen, then Screen 2 is paged in and Screen 1 is copied to Screen 2 (for assembler enthusiasts: LD HL,16384 / LD DE,49152 / LD BC,6912 / LDIR).

Sure, but who uses BASIC for such a small lace routine when you can do it just as well in machine code, and perhaps even a bit faster? I wrote the same thing again in assembly to make it usable with my Interruptmaker. However, you should note that all interrupt-driven routines that use bank switching should reside in an area below address 49152, and the CLEAR address (stack pointer) should also be below 49152. With a bit of luck, one could make this routine an exception, but I didn't feel like it (Tip: before this routine is called, load 23388 with the value of the bank in which this routine is located, otherwise it kicks itself out of memory).

When I first tested it with my Interruptmaker, the screen stayed black, so only after hours of troubleshooting did I learn a great deal about assembly and uncover the flaw in my logic. Yes, I had also called a tune in Bank 23 with the routine and then had to switch the bank back, but I hadn't taken bit 3 into account.

On this disk is my Interruptmaker 1.2. The improvements I have added are: by pushing IX and IY, you can finally access disks without crashing the system. Then I added the ability to switch banks, as described last time (the current bank must always be stored in 23388); an additional CALL before the bank is switched is also possible (the vector jump is then 3 bytes longer) in case someone wants to integrate the LACE routine. I also fixed the bug where the screen stayed black by adding the third bit (Screen) to the switch if it is also set in 23388. Now you can experiment a little with assembler!

The lace routine works both in USR mode (poke the current bank to address 23388) and in 128K mode, though not when interrupts are used. I already mentioned that 128K mode doesn't get along particularly well with interrupts (two's a crowd!).

Now for the lace assembler routine:

       ORG 40000       ; (or almost any other address)
       LD BC,32765     ; 1,253,127 Load BC with memory port.
       LD A,(23388)    ; 58,92,91  Load A with bank no.
       BIT 3,A         ; 203,95    Load Z flag with bit 3 of A (Scr)!
       JR Z,setz       ; 40,...    Jump to "setz" if bit=0.
       RES 3,A         ; 203,159   Clear bit 3 of the bank (Scr=0).
       JR last         ; 24,...    Jump to the last part.
setz:  SET 3,A         ; 203,223   Set bit 3 of the bank (Scr=1).
last:  OUT (C),A       ; 237,121   Switch bank.
       LD (23388),A    ; 50,92,91  Save modified bank number.
       RET             ; 201       Return to BASIC, but you could
                       ;           just as well insert a jump to a
                       ;           sound routine instead.

It should be noted that it is not the bank stored in memory address 23388 that gets switched, but merely bit 3; the bank itself remains the same, which is why you cannot alter the screen contents with POKE 49152 to 49152+6911—instead, you must place the bank into addressable memory using POKE 23388,23: OUT 32765,23. The second screen can remain visible even if it hasn't been paged in at all.

I only switched that single bit 3 in such a complicated way to demonstrate how it works, but of course it can be done much more simply, shorter, and faster:

       ORG 40000       ; Again, the program is fully relocatable.
       LD BC,32765     ; 1,253,127
       LD A,(23388)    ; 58,92,91
       XOR 8           ; 238,8     The third bit is XORed, i.e. flipped
       OUT (C),A       ; 237,121
       LD (23388),A    ; 50,92,91
       RET             ; 201

With XOR 8, the following happens: the current bank number (let's assume it was 16, because we are in bank 0 and the 48K ROM is paged in—ROM1, indicated by bit 4, so bank no. 0+16) is loaded into A: 00010000. XOR 8 causes the contents of the A register to be XORed with the value 8:

    00010000 = Dec 16
XOR 00001000 = Dec 8
  = 00011000 = Dec 24

It looks like a simple addition, but it isn't, because on the second call of the routine, this happens:

    00011000 = Dec 24
XOR 00001000 = Dec 8
  = 00010000 = Dec 16

So we get the original bank back. It is just as if the screen were modified with PLOT OVER 1—after the second pass, we get the original content back. XOR writes the result back into the A register. The exact mechanism is: all set bits of the XOR operand are inverted—1 becomes 0, and 0 becomes 1. With this single two-byte instruction, I was able to replace 5 other MC instructions and 10 bytes, and the execution time dropped from 91/96 T-states to 65 T-states.

For those expecting quick results, I made a BASIC loader that pokes this exact program into memory. You can choose any other address for it, as the second routine is also fully relocatable in memory.

Afterward, I coded a small demo with several lace images that I digitized on the Amiga and converted to lace there. It took long enough to figure out the right pixel rasters to maximize the effect; unfortunately, that is a time-consuming affair even on the Amiga. Even though I am fairly familiar with the Amiga and AMOS from my previous graphics converters, it was still a challenge.

Why did I go to such great lengths? I believe that under certain conditions, lace mode is also suitable for games—not necessarily for action games, but for strategy, puzzle games, adventures, RPGs, and similar genres where speed isn't overly critical. For example: one could equip Glücksrad (Wheel of Fortune) with digitized graphics from the TV show, or a classic murder mystery like "WHO KILLED COCK ROBIN" with high-quality images from the TV series "Murder, She Wrote". Maybe I'll write something like that someday.

If anyone wants the Amiga-to-Spectrum lace converter, you can send me a disk + DM 5,-. The hardware and software requirements would be: Spectrum side: disk interface with the ability to read MS-DOS disks, including the DOS copy program (+D, Disciple, OPUS, MB02), my IFF converter, ideally version 1.91. Amiga side: AMOS, Crossdos, Personal Paint (to convert the images down to 9 colors first).

That's it for this time. I hope that now that SCENE+ has more readers, you will also send your program ideas and sample programs to us. Well, who's up for it?

Yours, L. C. D.

Tip for Spectrum 128 and MB02 Users

MB02 is an outstanding interface in itself, but since it had to be made compatible with the 48K Specci, it always runs in the so-called USR 0 mode, where it is still possible to switch banks using OUT 32765. Unfortunately, there are also some programs that won't work with the Spectrum because of this, unless you press the BREAK key in the boot menu and then load from tape.

Specifically, these are 128K adventures written with PAW, which always display "OUT OF MEMORY" in USR 0 mode, as well as a few application programs like HiSoft Basic, which crash after the first COPY or CAT. And all sound demo programs that use the PLAY command (including HYPERSPACE 128). I cannot recommend a fix for this yet, but perhaps BUSYSOFT will address the issue and could write a new ROM.

What I do have a tip for is an entirely different problem: there are programs that refuse to work in USR 0 mode at all—they are guaranteed to crash. I don't want to sugarcoat it: none of the games I have written so far will survive it either. The culprit is my previous mouse driver, which for some unknown reason always crashes in USR mode.

So, for all those who own an MB02 with a Spectrum 128, the problem arises of how they can play my highly acclaimed INDIAN PATIENCE. The solution to the riddle is quite simple once you become familiar with the internal structure of the 128. With OUT 32765,48, you instruct the Spectrum to ignore bank switching from then on. Only after a RESET (not just pressing NMI and E! I mean a real reset!) is bank switching re-enabled.

I have learned from this and am currently writing a new mouse driver that

no longer exhibits this bug, which is also necessary, as at least one of my future games is supposed to be for Spectrum 128 only. I tried this trick with both of my MB02s (the early 128K and the newer 512K+Clock model), and it worked without problems on both, as well as without the MB02 in USR0Mode. In 128K Basic, however, the computer would crash! So, for games that only run in 48K mode, simply enter OUT 32765,48 before the last USR command, and the problem is solved.

Yours,

Share your thoughts about the article