Jump to content
Jet Set Willy & Manic Miner Community

source code for JSW


Norman Sword

Recommended Posts

JET SET WILLY SOURCE CODE



I mentioned a long time ago that I would eventually upload a source code for Jet Set Willy. This I have done and the code is in the download section, under "other resources".

The version is Raw6jsw.asm but the 6 is just indicating that  6 steps were needed in its evolution from a disassembly

Hope its of use to someone.

The listing will assemble in Pasmo and output the game.... 

Link to comment
Share on other sites

Just seen and approved it, thanks Norman. :)

 

Its generated its own 'support/comments' topic as most downloads submissions do, so we can either use this topic here you've started or that auto generated one if you prefer. I'll just simply close the unused one to prevent duplication/confusion.

 

EDIT... Decided to close the other 'auto generated' topic (with a link to this one) to use instead as its preferable I think, partly as its got more information and partly as its more up-to-date too.

Edited by Spider
Link to comment
Share on other sites

  • 2 weeks later...

Using the source code I provided. I spent a day changing and deleting code. This is an illustration of what can be achieved in a day.

Main point to this demo is the change in animation and definition of willy

 

 

 

 

Addendum:-

 

What else was changed

 

1) The clock routine. re written to correct the am/pm problem. (Smaller code)

2) sprite glitch and screen copying. Uses LDI and raster copy (visual update)
3) Constant update of status line and dancing willies. Only updates if needed (faster+smaller)

4) The room draw uses my method of expansion and no compares. (faster + a lot smaller)

5) deletion of all code asking for the colour code at the start (more free space)

6) rewrite of the cheat input (faster+smaller)

7) changed all the sprite routines (faster)

8) removed rope bug

9) Changed logo draw expansion routine (twice) (a lot smaller)

10)  changed method of colouring in the lower screen (a lot smaller)

11) changed every LDIR over 20 bytes to ldi (faster)

12) changed every keyboard reference to ports. Removed usage of BC registers when reading ports (consistent-bug removal-smaller)

 

How many variations of Willy, are their is in this demo. Ignoring the flying pig there are 16 definitions.



 

 

 

HeadsJSW.tap

Edited by Norman Sword
Link to comment
Share on other sites

The heads are stored in a table, which stores only one definition for each head. The clever part is to use a pile of code to extract the heads, and reverse and shift each definition and place it over the original.

Maybe not as noticeable but all willies animation is changed so that Willy has eight frames of animation in both directions.
  

Link to comment
Share on other sites

New code:- based on a quick re_write as illustrated by the small heads demo.

Part 1)

The original uses a big data table to colour in the status area of the screen. And a similar table to colour in the colour table entry screen. That table is just as easy to compress, but I will ignore that data here and concentrate only on the lower screen attribute area and its data.

The original lower screen data:- situated in memory in the original game at $9a00

;9A00: Attributes for the bottom third of the screen

bottom_att
DEFB $46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46 
DEFB $46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $01,$02,$03,$04,$05,$06,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07 
DEFB $07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$06,$05,$04,$03,$02,$01 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $45,$45,$06,$06,$04,$04,$41,$41,$05,$05,$43,$43,$44,$44,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $45,$45,$06,$06,$04,$04,$41,$41,$05,$05,$43,$43,$44,$44,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 

This data was moved to the lower screen with:-

Code at $8907

LD HL,bottom_att ; 8907 ;L9A00 
LD DE,ATT16       ; 890A ;$5A00
LD BC,$0100       ; 890D ;
LDIR                   ; 8910 ;

 

and code at $8b07

 

LD HL,bottom_att ;L8B07 ;L9A00  
LD DE,ATT16      ; 8B0A ;$5A00
LD BC,$0100       ; 8B0D ;
LDIR

Both of these LDIR routines are replaced with a call to draw_bottom_att which is new code sitting in ram somewhere.

This code is

draw_bottom_att
    ld de,ATT16
    ld ix,bottom_att
    ld hl,bottom_translate


; expander sub
expand:
    ld (S_M_C_translate),hl
draw_logo_loop
    ld a,(ix)
    inc ix
    cp 255
    ret z
S_M_C_translate equ $+1
    ld hl,bottom_translate                ;logo_translate  << edited to remove this undefined label (in the code here)
    call indexer

expand1

    ld (de),a
    inc de
    djnz expand1
    jr draw_logo_loop
;=======================================================================

; expander sub routine
indexer
    ld c,a
    rrca
    rrca
    rrca
    rrca
    and 15
    inc a
    ld b,a

    ld a,c
    and 15

 

; + index into hl via "a" and return (hl+"a") in "a"
index1
    add a,l
    ld l,a
    adc a,h
    sub l
    ld h,a
    ld a,(hl)
    ret

Looking at the lines and lines of code used above, you might be left wondering how all that saves any memory compared to a simple LDIR.

 

A very quick count of the code comes in at 53 bytes. Now remember the original data was 256 bytes, so we need to be able to save at least 53 bytes. Any more than that and it is smaller in size. But we also get a subroutine which can be used in various ways as a bonus.

So here is the original compressed 

 

bottom_att

db   $f8,$f8
db   $f0,$f0
db   $f0,$f0
db   $01,$02,$03,$04,$05,$06,$97,$97,$06,$05,$04,$03,$02,$01
db   $f0,$f0
db   $19,$16,$14,$1a,$15,$1b,$1c,$10,$f0
db   $19,$16,$14,$1a,$15,$1b,$1c,$10,$f0
db   $f0,$f0
db   $ff

 

bottom_translate:
;   0 1     2     3    4     5     6    7    8    9    a     b     c 
db 0,$01,$02,$03,$04,$05,$06,$07,$46,$45,$41,$43,$44

The above is less than 60 bytes (around 56) which means the code plus the data is less than 120 bytes. a saving of over 130 bytes.

It is possible to use this method to compress the logo. (with decent gains, but there are better ways)
 

Edited by Norman Sword
Link to comment
Share on other sites

Part 2)


I write code out of curiosity. So after the quick attribute compress. I wondered how much that routine would compress the logo. The methods I have used before are different in the way they compress, but sometimes just taking a different path shows a better route. So the curiosity part of me did the compression.

These compression's are done from visual translations. e.g. I look at the data and work the numbers out.

The observant will have noticed that in part 1) there is a label called "draw_logo_loop" and a reference to "logo_translate", which was a good indication that I had written the code and worked out the data.

so here is the same kind of data  and code for the logo as for the bottom attributes

    ld de,ATT0
    ld ix,LOGO_DAT
    ld hl,logo_translate
    call expand                       ; the routine shown in part 1

 

 

logo_translate:

;---------- 0---1----2----3----4-----5----6----7----8-----9----a----b--- c
     db $00,$28,$05,$d3,$2d,$25,$24,$09,$29,$2c,$04,$08

 

;compressed top screen attributes for the logo
LOGO_DAT
    DB $f0,$f0
    DB $f0,$f0
    DB $f0,$10,$11,$12,$90
    DB $30,$23,$00,$23,$00,$23,$00,$01,$23,$05,$23,$00,$23,$30
    DB $40,$03,$10,$03,$30,$03,$11,$04,$03,$15,$06,$03,$30,$03,$40
    DB $40,$03,$10,$23,$00,$01,$03,$14,$05,$23,$06,$23,$10,$03,$40
    DB $40,$03,$10,$03,$00,$11,$04,$03,$15,$16,$0a,$03,$06,$03,$30,$03,$40
    DB $30,$13,$10,$23,$04,$05,$03,$16,$0a,$23,$06,$23,$10,$03,$40
    DB $70,$18,$14,$19,$1a,$10,$17,$16,$90
    DB $70,$17,$18,$14,$12,$10,$17,$16,$90
    DB $50,$03,$00,$1b,$03,$07,$03,$08,$03,$04,$12,$03,$07,$06,$03,$20,$03,$50
    DB $50,$03,$20,$03,$0b,$03,$07,$03,$08,$14,$03,$07,$06,$03,$20,$03,$50
    DB $50,$03,$00,$03,$00,$03,$00,$03,$0b,$03,$07,$18,$03,$07,$06,$43,$50
    DB $50,$03,$00,$03,$00,$03,$00,$03,$00,$03,$0b,$17,$03,$07,$16,$00,$03,$70
    DB $50,$43,$00,$03,$00,$13,$03,$0b,$23,$06,$00,$03,$70
    DB $f0,$10,$1b,$1a,$90
    DB $ff

 

Not bad in compression the logo translate uses 12 bytes, while the logo_dat uses vastly less than the 512 bytes of the original. (in the region of 192 bytes (exact figure is not important))  A possible saving of around 430 bytes for this data and the bottom attribute data.

whilst this did work, I have modified the data at some point. And it might not work properly now. (it will be very close)

What this illustrated to me was my other methods do a better job. 

In part 2b. I will re_write the part of the routine that draws the logo on the screen, this will save some more memory. 

 

Edited by Norman Sword
Link to comment
Share on other sites

Addendum:- to the compression of data part 1 and 2

I made no attempt at explaining what the compression routine is doing and no attempt at how I manage to compress the data. I started off by just looking at the data and trying to see patterns. No obvious patterns were evident. I then looked at the spread of data. This is just looking at how many values are in the data that I want to compress. In the lower screen attribute data there are less than 16 different values, so I can immediately assign 4 bits to the spread of data. This leaves four bits in a byte. Which has a range of 0 to 15. If I assign those four bits as a counter then I can increase in range from 1 to 16.  There being no point in including a nothing count.

 

The method picked also needs the data to be repetitious, having long enough runs of data to have a running count. If the data changes value often, then this method starts to fail. The failure means the data does not compress, but does not take up more space than the uncompressed data. ( this ignores the code to uncompress the data)

 

Now lets look at some of the data

DEFB $46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46       ;line 1
DEFB $46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46,$46       ;line 2
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00       ;line 3
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00       ;line 4
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00       ;line 5
DEFB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00       ;line 6

DEFB $01,$02,$03,$04,$05,$06,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07       ;line 7

 

bottom_translate:

 

;--- 0---1----2----3----4-----5----6----7----8-----9----a----b--- c
db 0,$01,$02,$03,$04,$05,$06,$07,$46,$45,$41,$43,$44

 

Notice bottom translate has a row of numbers written above each of the values in the DB statement. These numbers correspond to the numbers position in the data.

The values in the bottom translate are all the differing values in the data we are compressing.

 

Now looking at the data:-

The line1 comprises of 16 identical $46. From the bottom translate table we can see that $46 is at an offset of 8


 

 

To compress line1 of data we write $F8.

       The $Fx part of the compression represents the value repeated $f times more- 16 values in total 

       The $x8 part represents the value at offset 8 in the bottom translate data - $46
To compress line2 of data we write $f8 again

       The $Fx part of the compression represents the value repeated $f times more - 16 values in total
       The $x8 part represents the value at offset 8 in the bottom translate data - $46

To compress line3 of data we write $F0

       The $Fx part of the compression represents  the value repeated $f times more - 16 values in total
       The $x0 part represents the value at offset 0 in the bottom translate data - $00

To compress line4 of data we write $F0
       The $Fx part of the compression represents the value repeated $f times more - 16 values in total
       The $x0 part represents the value at offset 0 in the bottom translate data - $00

To compress line5 of data we write $F0
       The $Fx part of the compression represents the value repeated $f times more - 16 values in total
       The $x0 part represents the value at offset 0 in the bottom translate data - $00

To compress line6 of data we write $F0
       The $Fx part of the compression represents the value repeated $f times more - 16 values in total
       The $x0 part represents the value at offset 0 in the bottom translate data - $00

 

line7 gets a bit more complicated, here the data is changing and we do not have big repeats so compression breaks down.

the first value on line 7 is $01 this compresses to $01

       The $0x part of the compression represents the value  repeated $0 times more - 1 value in total

       The $x1 part represents the value at offset 1 in the bottom translate data - $01

the second value on line 7 is $02 this compresses to $02
       The $0x part of the compression represents the value repeated $0 times more - 1 value in total
       The $x2 part represents the value at offset 2 in the bottom translate data - $02

the third value on line 7 is $03 this compresses to $03
       The $0x part of the compression represents the value repeated $0 times more - 1 value in total
       The $x3 part represents the value at offset 3 in the bottom translate data - $03

 

we can slowly go through all the data and visually compress the data

 

 

 

 

Edited to reformat some data - the data layout is compromised by proportional spaced fonts

   

Edited by Norman Sword
Link to comment
Share on other sites

Part 3)

 

I have not forgotten part 2a. 

 

(numbers quoted are approximations for the size of data fields. They are approximations not definite numbers)

 

 

We are drawing the impossible triangle logo on the screen. And for most new iterations of the game we change the graphics to incorporate a new heading text.

 

This is ok if we are restricting ourselves to one boring title screen. But it necessitates redrawing the logo each time to have the new text in it. It is easier to separate the logo from the text graphics. Then any editing done to the text, does not change the way we store the logo. We can compress the logo and leave the logo alone for each and every edit. We can also have multiple title screens and the logo can be placed behind any of the new text we generate.

 

This at first looks to be counter productive. We now have two sets of data to compress and probably need two routines and two sets of set up code. One for the logo and one for the text. I admit it does seem strange, but I wanted the ability to have multiple screens, and separating the data and the text overlay revealed a feature of the logo that has been buried due to the nature of the graphics data presented. This is the revelation that the logo is generated from repeating graphics. Every value in the logo data is repeated. and this revelation means we can do an instant halving of the logo data. Yes the immediate reduction from 512 bytes of data to only 256 bytes of data, just because of this fact.

 

We can then off course compress the half size logo. This will reduce the data to 84 (approximate) bytes... Read that number again 84 (approximate) bytes of data for the naked logo. Which makes the original 512 bytes data look a big waste of space. But hang on we now have no text over laying the logo. We need to add that data onto the logo data.

 

The text overlay spoils the figures. The text overlay will consume a lot of data and we do need a quick and easy method of changing the text. Unless you are up to writing yourself an editor to do so.

I settled on a simple bit pattern for the overlay text data, one that is visual and easy to edit. Pasmo the assembler does not make the layout of this data easy, but it is still easy enough to edit.

 

The layout consists of Bits within a byte. Since a byte has eight bits, we can write out four bytes in binary and it can represent the 32 attribute squares across the screen. This data table for the text will use 16*4=64 bytes to define the overlay of text on the screen. It is data that is easy to edit without any other help.

 

logo_overlay

db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00100010b,11111000b,10001111b,00011110b
db 00100010b,10001001b,01001000b,10100000b
db 00111110b,11100010b,00101000b,10011100b
db 00100010b,10001011b,11101000b,10000010b
db 00100010b,11111010b,00101111b,00111100b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b
db 00000000b,00000000b,00000000b,00000000b    

db $ff ; manual inspection for value

 

Proportional spaced font, as used in this forum will change the look of the data presented above. But I hope you can still read the word HEADS in the binary data.

 

And finally we need a method of placing the above data on top of the expanded logo data. Which is as follows.

 

text_overlay:
    ld hl,ATT0
    ld de,logo_overlay
   ld c,FLASH+RED+8*YELLOW

text:

    ld a,(de)

    cp 255
    jr z,graphic   ; or return (depends on how this is set up)
    ld b,8  ;width of data byte
text1:

    rlca   ;bit out
    jr nc,clear
    ld (hl),c
clear:

    inc hl
    djnz text1
    inc de
    jr text

 

The end of the routine

graphic:

 

; the routine above is just 25 bytes (approximate)

-----------------------------------------------------------------------------------

 

So we can draw the logo with its overlay of text in this sequence

 

Draw logo (84 bytes approx of data) onto the attribute area of the screen 

overlay the text (64 bytes of text) onto the attribute area of the screen.

then translate the combined attribute data into the graphics needed                  (part 2b)

The two expansion routine are 53 (approx) for the logo draw routine

and 25 bytes for the logo overwrite routine.

 

------------------------------------------------------------------------

 

A new screen only requires new text data, so we can generate a new logo screen for 64 bytes plus its set up data.

We still save hundreds of bytes on the basic data.....

Edited by Norman Sword
Link to comment
Share on other sites

Part 2a:

The original logo draw and expansion does a lot of checking for the various colours. The result of all these checks is the decision on how any graphic is finally drawn to the screen.  There are in the region of 13 differing coloured blocks, and the code decides what it is going to draw based on the colour of the block.

The blocks defined seem to be laid out in no order, and the extraction of the blocks to either draw a graphic one way or draw a graphic the other seems to be haphazard. Then we have the problem that one of the colour blocks is changed in colour as it is looked at.

Looking at the part that draws the logo from the attributes we can see the various checks being made at the start of the routine. We need a better look at these checks rather than just using checks for specific numbers.

The first set of checks are actually looking at two things.

    OR A  
    JR Z,logo_skip 
    CP $D3   
    JR Z,logo_skip
    CP $09
    JR Z,logo_skip 
    CP $2D  
    JR Z,logo_skip
    CP $24 
   JR Z,logo_skip

These checks are concerned with either the flashing logo letters -- Value $d3 or any colour tile where the ink and paper are the same colour. So Instead of doing checks for specific colours I will check for ink and paper match instead. The $d3 match is best changed to a more vague check of flashing ink/paper. This allows the colour to be changed with one instruction at the part the draws the giant text. (assuming we have split the giant text from the logo)

Next we have a lot of checks for the various slope graphics. and a special case where the colour is changed. By looking at the data and looking at what is wanted, we can see that the reason we have a special case colour is because that single colour is odd compared to the rest. When that odd colour tile is drawn it ends up as a sloped tile with the same colour as some other sloped tiles. But slopping in the opposite direction. Which is why it is singled out and treated differently.

It is easier and more logical to use the built in attributes of the tiles. The logo itself is drawn with no BRIGHT attributes. so instead of all the various checks needed to draw the logo. Lets just use the BRIGHT bit as a simple indicator for the direction of the slope. So if the tile has the BRIGHT bit it slopes one way and a tile without the BRIGHT bit can slope the other way. Doing this removes the special case tile. The tile has its colour changed to what it should be. After checking the tile for the BRIGHT bit, The bit is immediately switched off.

This changes the draw routine to the following.
 

logo_graphics:
; this part adds the graphics
    LD DE,ATT0 
logo_draw
    LD A,(DE) 
    bit 7,a
    jr nz,logo_skip
    ld c,a
    rrca
    rrca
    rrca
    xor c
    and 7
    jr z,logo_skip
    ld a,c
    ld hl,triangle_udg
    bit 6,a
    jr z,logo1_graphic
    ld hl,triangle_udg+16
    and 10111111b 
    ld (de),a
logo1_graphic
    ld a,e
    rlca
    rlca
    rlca
    and 8
call index1  ; add a to hl returns a=(a+hl) ;ignore the return "a"
    ld c,d 
    BIT 0,D
    LD D,high CHAR0
    JR Z,Llow
    LD D,high CHAR8 
Llow
    CALL printing_char  ;the instruction before the normal call, which loads b with 8
    ld d,c
logo_skip:
    INC DE
    LD A,D
    CP high +(ATT16)
   Jr NZ,logo_draw
   ret                              ;In my code this is a subroutine

; this uses slightly different data.  from the original.

 

Part 4 is all the code placed together. (a working example)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.