How did Treasure Island work?

41 views
Skip to first unread message

Groo Vee

unread,
Jul 16, 2020, 3:29:25 AM7/16/20
to
There was a game, I'm _99%_ certain it was called Treasure Island, which had the top half of the screen blue and the bottom black (or maybe it was the other way around) INCUDING THE BORDER!! ie. the ENTIRE SCREEN'S TOP HALF was blue!!! This continued WHILE YOU PLAYED THE GAME!!!!!! It blew my mind at the time, so today, so many years later, I'd love to know how the HELL they pulled that off? What's going on?


Thanks.

Volker Bartheld

unread,
Jul 16, 2020, 6:30:06 AM7/16/20
to
Maskable interrupt handler [1], typically of the IM2 type (vectored
interrupt) where - as opposed to the first example in [1] - the complete
table is filled with #FEFE, so your interrupt handler always starts at
address #FEFE, no matter what lower byte is provided by the "peripheral
device" on the bus (since there usually is none or some Kempston joystick
interface producing garbage).

At #FEFE you would then have

ORG #FEFE
; no need to di, Z80 does this automatically
push af ; save carry flag
push bc ; save bc register

set border to blue

ld a,#01 ; blue border
out (#FE),a ; change color

and wait a multiple of 48+176=224 T-States [2]

ld bc,6408h
wait: nop ; 4T
nop ; 4T
nop ; 4T
dec bc ; 6T
jp nc, wait ; 10T
; 28T for each loop, so 8 iterations gets you down one scan line

then change the border color to black

ld a,00h ; black border
out (FEh),a ; change color

, restore the registers and return from the interrupt handler

pop bc ; restore bc
pop af ; restore af
ei ; re-enable interrupts
reti ; return

Hint: Don't ever use HALT in an interrupt handler and stay away from
contended memory (everything below #8000 is managed by the ULA). Have a
look at R. Zak's book "Programming the Z80" for details on machine code
timing [3].

Of course the example above is far from perfect (needs some nudging back
and forth to perfectly syncronize with the start of a scanline, you could
use OUTI/OTIR for even crazier stuff) and you can do this not only to the
border, but also the attribute area (using LDI/LDIR), getting you
multicolored attributes that overcome the 8x8 pixel limit.

This is what I wrote, decades ago. It even returns you to ZX BASIC with the
highres attributes on.

10 CLEAR 65369
20 LET d=65370
30 FOR a=1 TO 33
40 LET c=0
50 READ a$
60 READ check
70 FOR e=1 TO 10 STEP 2
80 LET b=(CODE a$(e)-48-(7 AND CODE a$(e)>57))*16
90 LET b=b+CODE a$(e+1)-48-(7 AND CODE a$(e+1)>57)
100 PRINT AT 10,0;d,b,: POKE d,b: LET d=d+1: LET c=c+b: NEXT e
110 IF d>65535 THEN GO TO 160
120 IF c<>check THEN PRINT "CHECKSUMERROR IN LINE ";(a-1)*10+9000: LET f=(a-1)*10+9000: LIST f: STOP
130 NEXT a
140 CLS : PRINT "HIGHRESOLUTION COLOUR ATTRIBUTES"'''
150 PRINT "START:",65370'"LENGTH:",166'"INTERRUPT ON:",65370'"INTERRUPT OFF:",65384'''"LABELS:"'"STARTLN:",65393;" L=1"'"DEPTH:",65394;" L=1"'"HIATT:",65395;" L=2"'"IHANDLE:",65524
160 STOP
170 SAVE *"m";1;"HIGHRES-I"CODE 65370,166: VERIFY *"m";1;"HIGHRES-I"CODE
180 STOP
9000 DATA "F321FFFF36",840
9010 DATA "183E3BED47",453
9020 DATA "ED5EFBC9F3",1026
9030 DATA "3E3FED47ED",670
9040 DATA "56FBC90018",562
9050 DATA "0000000000",0
9060 DATA "00C5D5E5F5",884
9070 DATA "ED7375FF31",773
9080 DATA "79FF011802",403
9090 DATA "0B78B120FB",591
9100 DATA "3A71FF8787",696
9110 DATA "87CAA0FF06",758
9120 DATA "0F10FE0000",285
9130 DATA "C83DC295FF",859
9140 DATA "6F3A71FF67",640
9150 DATA "CB3CCB1DCB",698
9160 DATA "3CCB1DCB3C",555
9170 DATA "CB1D110C58",349
9180 DATA "19EB2A73FF",672
9190 DATA "3A72FF0140",492
9200 DATA "00D5EDA0ED",847
9210 DATA "A0EDA0EDA0",954
9220 DATA "EDA0EDA0ED",1031
9230 DATA "A0EDA0D1E2",992
9240 DATA "DBFF180000",498
9250 DATA "E6FF18E4EB",972
9260 DATA "0E2009EB3D",351
9270 DATA "C2BCFFED7B",997
9280 DATA "75FFF1E1D1",1047
9290 DATA "C1FBC33800",695
9300 DATA "00000000F3",243
9310 DATA "C379FF0000",571
9320 DATA "0000000000",0
9330 DATA "0000000000",0
9340 DATA "0000000000",0

which disassembles as (no warrantee, no comments):

ORG 0FF5Ah

DI
LD HL,0FFFFh
LD (HL),18h
LD A,3Bh
LD I,A
IM 2
EI
RET

LFF68: DI
LD A,3Fh
LD I,A
IM 1
EI
RET

LFF71: NOP
LFF72: JR LFF74

LFF74: NOP
LFF75: NOP
NOP
NOP
NOP
LFF79: PUSH BC
PUSH DE
PUSH HL
PUSH AF
LD (LFF75),SP
LD SP,0FF79h
LD BC,0218h
LFF87: DEC BC
LD A,B
OR C
JR NZ,LFF87
LD A,(LFF71)
ADD A,A
ADD A,A
ADD A,A
JP Z,LFFA0
LFF95: LD B,0Fh
LFF97: DJNZ LFF97
NOP
NOP
RET Z
DEC A
JP NZ,LFF95
LFFA0: LD L,A
LD A,(LFF71)
LD H,A
SRL H
RR L
SRL H
RR L
SRL H
RR L
LD DE,580Ch
ADD HL,DE
EX DE,HL
LD HL,(LFF72+1)
LD A,(LFF72)
LFFBC: LD BC,0040h
LFFBF: PUSH DE
LDI
LDI
LDI
LDI
LDI
LDI
LDI
LDI
POP DE
JP PO,LFFDB
JR LFFD6

LFFD6: NOP
AND 0FFh
JR LFFBF

LFFDB: EX DE,HL
LD C,20h
ADD HL,BC
EX DE,HL
DEC A
JP NZ,LFFBC
LD SP,(LFF75)
POP AF
POP HL
POP DE
POP BC
EI
JP 0038h

LFFF0: NOP
NOP
NOP
NOP
DI
JP LFF79

Also,

https://www.youtube.com/watch?v=eGDLdf3nZL0
https://www.youtube.com/watch?v=wE1KylKQDrQ
https://www.youtube.com/watch?v=79sg7UE6KAA
https://www.youtube.com/watch?v=VB3mDqGgBag

should give you a good impression of what is possible.

HTH,
Volker

[1] https://codersbucket.blogspot.com/2015/04/interrupts-on-zx-spectrum-what-are.html
[2] http://www.zxdesign.info/interrupts.shtml
[3] http://www.z80.info/zaks.html

Chupo

unread,
Aug 26, 2020, 11:08:33 PM8/26/20
to
In article <csj6yfu7...@news.bartheld.net>, Volker Bartheld
<news...@bartheld.net> says...
I checked the videos because I find IM 2 routines very interesting and
was surprised to see the IM 2 routine from the 1st video was by me :-)

From your code I see you are using the same trick by placing interrupt
vector in the ROM where all bytes are #FF and putting the opcode of the
JR instruction (#18) at #FFFF which then takes byte #F3 (opcode of DI
at address #0000) as relative jump offset of -13 and after:

JR $-13

the program continues at 65524 (#FFF4).

The only difference is you load I register with #3B and I've been
always using #39 where there are RST #38s (#FFs) too.

This is how I've been setting up IM 2:

di
ld a,#39
ld i,a
ei
ret

org #fff4
jp int_addr

org #ffff
defb #18
--
Let There Be Light
Custom LED driveri prema specifikacijama
http://tinyurl.com/customleddriver

Chupo

Volker Bartheld

unread,
Aug 27, 2020, 2:02:05 AM8/27/20
to
On Thu, 27 Aug 2020 05:08:35 +0200, Chupo wrote:
> In article <csj6yfu7...@news.bartheld.net>, Volker Bartheld
> <news...@bartheld.net> says...
>> https://www.youtube.com/watch?v=eGDLdf3nZL0
>> https://www.youtube.com/watch?v=wE1KylKQDrQ
>> https://www.youtube.com/watch?v=79sg7UE6KAA
>> https://www.youtube.com/watch?v=VB3mDqGgBag
>> should give you a good impression of what is possible.
> [Spectrum and IM 2] From your code I see you are using the same trick by
> placing interrupt vector in the ROM where all bytes are #FF and putting
> the opcode of the JR instruction (#18) at #FFFF which then takes byte
> #F3 (opcode of DI at address #0000) as relative jump offset of -13 and
> after: JR $-13 the program continues at 65524 (#FFF4).

That's 100% correct. I found this a very obvious and straightforward
approach, given the fact that the lower half of the IM 2 jump location is
provided by the ZX Spectrum's bus, which could be anything at that time.
Saves you a table of 256 useless Bytes in precious RAM.

Greets,
Volker
Reply all
Reply to author
Forward
0 new messages