Added patched version for Commodore Plus/4
This commit is contained in:
parent
572fde30d7
commit
f5a52fcab5
13 changed files with 789 additions and 4 deletions
23
README.md
23
README.md
|
|
@ -26,6 +26,7 @@ https://user-images.githubusercontent.com/2428251/187879166-74e973e3-aa49-40e3-b
|
||||||
|
|
||||||
* [A better algorithm](#a-better-algorithm)
|
* [A better algorithm](#a-better-algorithm)
|
||||||
* [The patching process](#the-patching-process)
|
* [The patching process](#the-patching-process)
|
||||||
|
* [Patching the Commodore Plus/4 version][#patching-the-commodore-plus-4-version]
|
||||||
|
|
||||||
* [Building the patch](#building-the-patch)
|
* [Building the patch](#building-the-patch)
|
||||||
|
|
||||||
|
|
@ -40,12 +41,14 @@ BBC Master Elite was written by Ian Bell and David Braben and is copyright © Ac
|
||||||
|
|
||||||
The code in the flicker-free patch was reconstructed from a disassembly of the BBC Master version released on [Ian Bell's personal website](http://www.elitehomepage.org/).
|
The code in the flicker-free patch was reconstructed from a disassembly of the BBC Master version released on [Ian Bell's personal website](http://www.elitehomepage.org/).
|
||||||
|
|
||||||
The game disks in this repository are very similar to those released on [Ian Bell's personal website](http://www.elitehomepage.org/), but to ensure accuracy to the released versions, I've used disk images from the [Commodore 64 Preservation Project](https://archive.org/details/C64_Preservation_Project_10th_Anniversary_Collection) (it turns out that the disk images on Ian Bell's site differ slightly from the official versions).
|
The Commodore 64 game disks in this repository are very similar to those released on [Ian Bell's personal website](http://www.elitehomepage.org/), but to ensure accuracy to the released versions, I've used disk images from the [Commodore 64 Preservation Project](https://archive.org/details/C64_Preservation_Project_10th_Anniversary_Collection) (it turns out that the disk images on Ian Bell's site differ slightly from the official versions). The Commodore Plus/4 version is based on the disk image from Ian Bell's site.
|
||||||
|
|
||||||
The commentary is copyright © Mark Moxon. Any misunderstandings or mistakes in the documentation are entirely my fault.
|
The commentary is copyright © Mark Moxon. Any misunderstandings or mistakes in the documentation are entirely my fault.
|
||||||
|
|
||||||
Huge thanks are due to the original authors for not only creating such an important piece of my childhood, but also for releasing the source code for us to play with. Also, a big thumbs up to Kroc Camen for his epic [Elite Harmless](https://github.com/Kroc/elite-harmless) project, which is a really useful reference for anyone exploring the C64 binaries. Finally, thanks to the gurus in this [Lemon64 forum thread](https://www.lemon64.com/forum/viewtopic.php?t=67762&start=90) for their sage advice.
|
Huge thanks are due to the original authors for not only creating such an important piece of my childhood, but also for releasing the source code for us to play with. Also, a big thumbs up to Kroc Camen for his epic [Elite Harmless](https://github.com/Kroc/elite-harmless) project, which is a really useful reference for anyone exploring the C64 binaries. Finally, thanks to the gurus in this [Lemon64 forum thread](https://www.lemon64.com/forum/viewtopic.php?t=67762&start=90) for their sage advice.
|
||||||
|
|
||||||
|
For the Commodore Plus/4 version, I am indebted to @Kekule1025 on Twitter, who extracted the original game from Pigmy's binaries, and wrapped it back up once I had finished adding the patch. Thank you Kekule.
|
||||||
|
|
||||||
You can find more information about my own Elite project in the [fully documented source code for Elite on the BBC Micro](https://www.bbcelite.com).
|
You can find more information about my own Elite project in the [fully documented source code for Elite on the BBC Micro](https://www.bbcelite.com).
|
||||||
|
|
||||||
### A note on licences, copyright etc.
|
### A note on licences, copyright etc.
|
||||||
|
|
@ -70,7 +73,11 @@ To play the flicker-free version of Commodore 64 Elite, you need to download a d
|
||||||
|
|
||||||
[See here](http://unusedino.de/ec64/technical/misc/vic656x/pal-ntsc.html) for a brief technical summary on the differences between PAL and NTSC on the Commodore 64.
|
[See here](http://unusedino.de/ec64/technical/misc/vic656x/pal-ntsc.html) for a brief technical summary on the differences between PAL and NTSC on the Commodore 64.
|
||||||
|
|
||||||
These images have been tested in the [VICE emulator](https://vice-emu.sourceforge.io) and in a number of online emulators, such as [C64 online](https://c64online.com/c64-online-emulator/) and [Virtual Consoles](https://virtualconsoles.com/online-emulators/c64/). They should also work on real machines. If you don't know which one to use, try the PAL version first, as that seems to be the default setting for most emulators. Both disk images are based on the GMA86 release of Elite from 1986.
|
There is also a version for the Commodore Plus/4, which is based on Pigmy's unofficial version:
|
||||||
|
|
||||||
|
* [Download flicker-free Commodore Plus/4 Elite as a .prg file](https://github.com/markmoxon/c64-elite-flicker-free/raw/master/flicker-free-disks/elite_+4_flicker_free.prg)
|
||||||
|
|
||||||
|
All these images have been tested in the [VICE emulator](https://vice-emu.sourceforge.io) and in a number of online emulators, such as [C64 online](https://c64online.com/c64-online-emulator/) and [Virtual Consoles](https://virtualconsoles.com/online-emulators/c64/). They should also work on real machines. If you don't know which one to use, try the PAL version first, as that seems to be the default setting for most emulators. Both disk images are based on the GMA86 release of Elite from 1986.
|
||||||
|
|
||||||
Saved commander files should work in exactly the same way as in the original GMA86 version; the only changes in the patch are graphical, and they don't affect gameplay in any way.
|
Saved commander files should work in exactly the same way as in the original GMA86 version; the only changes in the patch are graphical, and they don't affect gameplay in any way.
|
||||||
|
|
||||||
|
|
@ -119,6 +126,18 @@ To find out more about the above steps, take a look at the following files, whic
|
||||||
|
|
||||||
The commentary in these files is best read alongside the code changes, which are described in detail in the article on [backporting the flicker-free algorithm](https://www.bbcelite.com/deep_dives/backporting_the_flicker-free_algorithm.html).
|
The commentary in these files is best read alongside the code changes, which are described in detail in the article on [backporting the flicker-free algorithm](https://www.bbcelite.com/deep_dives/backporting_the_flicker-free_algorithm.html).
|
||||||
|
|
||||||
|
### Patching the Commodore Plus/4 version
|
||||||
|
|
||||||
|
The Commodore Plus/4 version of Elite is an unofficial release of the game that was converted from the Commodore 64 version by Pigmy. You can find lots of information about the game on [Commodore Plus/4 World](http://plus4world.powweb.com/software/Elite_Plus4).
|
||||||
|
|
||||||
|
The patching process follows a similar set of steps to the Commodore 64 version, but it operates on a game binary that's already been extracted from Pigmy's original version (thank you to @Kekule1025 for doing this, and for packing the final game up after I'd done my patching). The game runs at a different address to the Commodore 64 version, so the [`elite-flicker-free-plus4.asm`](src/elite-flicker-free-plus4.asm) and `elite-modify-plus4.py`](src/elite-modify-plus4.py) files modify the code in different places to the Commodore 64 version. Most (though not all) routines run at addresses that are $0900 higher in memory than their Commodore 64 counterparts, so that's why you can see `+ $0900` throughout these files.
|
||||||
|
|
||||||
|
Also, because the Pigmy version comes with a demo loading screen that takes up a fair amount of extra memory, we can't just tack the flicker-free routines onto the end of the game binary, as we do in the Commodore 64 version. Instead we can put them in the spite area, and specifically over the top of the two Trumble sprites, which are not used in the Plus/4 version (the Plus/4 does contain Trumbles, but because the machine does not support hardware sprites, they do not appear on-screen, and the sprite definitions are unused).
|
||||||
|
|
||||||
|
The build process for the Plus/4 creates a file called `elite_+4_modified.prg` in the `work` folder that contains the modified game (you can load this into an emulator, and run it with a `SYS 20736`' command, as the game code starts at $5100). The downloadable version is wrapped in Pigmy's original demo and packing code, which is a process that is out of the scope of this site (I don't know how @Kekule1025 did it!).
|
||||||
|
|
||||||
|
Apart from these differences, the patching process is the same as for the Commodore 64 version.
|
||||||
|
|
||||||
## Building the patch
|
## Building the patch
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
|
||||||
18
build.sh
18
build.sh
|
|
@ -39,7 +39,7 @@ $c1541 \
|
||||||
cd ..
|
cd ..
|
||||||
$python src/crc32.py reference-binaries/ntsc work
|
$python src/crc32.py reference-binaries/ntsc work
|
||||||
|
|
||||||
# And now, we build the PAL version
|
# Next, we build the PAL version
|
||||||
cd work
|
cd work
|
||||||
|
|
||||||
# Extract the files from the original disk image
|
# Extract the files from the original disk image
|
||||||
|
|
@ -69,3 +69,19 @@ $c1541 \
|
||||||
# Report checksums
|
# Report checksums
|
||||||
cd ..
|
cd ..
|
||||||
$python src/crc32.py reference-binaries/pal work
|
$python src/crc32.py reference-binaries/pal work
|
||||||
|
|
||||||
|
# And finally we modify the Plus/4 version
|
||||||
|
cd work
|
||||||
|
|
||||||
|
# Copy the decrypted PRG to the work folder
|
||||||
|
cp ../original-disks/elite_+4_unpacked.prg .
|
||||||
|
|
||||||
|
# Assemble the additional code required for flicker-free ships
|
||||||
|
$beebasm -i ../src/elite-flicker-free-plus4.asm -v > compile.txt
|
||||||
|
|
||||||
|
# Modify the main game code
|
||||||
|
$python ../src/elite-modify-plus4.py
|
||||||
|
|
||||||
|
# Report checksums
|
||||||
|
cd ..
|
||||||
|
$python src/crc32.py reference-binaries/plus4 work
|
||||||
|
|
|
||||||
BIN
flicker-free-disks/elite_+4_flicker_free.prg
Normal file
BIN
flicker-free-disks/elite_+4_flicker_free.prg
Normal file
Binary file not shown.
BIN
original-disks/ELITEPIG.PRG
Normal file
BIN
original-disks/ELITEPIG.PRG
Normal file
Binary file not shown.
BIN
original-disks/elite_+4_unpacked.prg
Normal file
BIN
original-disks/elite_+4_unpacked.prg
Normal file
Binary file not shown.
BIN
reference-binaries/plus4/elite_+4_modified.prg
Normal file
BIN
reference-binaries/plus4/elite_+4_modified.prg
Normal file
Binary file not shown.
BIN
reference-binaries/plus4/extra-plus4.bin
Normal file
BIN
reference-binaries/plus4/extra-plus4.bin
Normal file
Binary file not shown.
BIN
reference-binaries/plus4/ll155-plus4.bin
Normal file
BIN
reference-binaries/plus4/ll155-plus4.bin
Normal file
Binary file not shown.
1
reference-binaries/plus4/ll78-plus4.bin
Normal file
1
reference-binaries/plus4/ll78-plus4.bin
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
¥ûÅ0°¥[i…[<5B>æ\柤ŸÄ®°L¶¨ê
|
||||||
1
reference-binaries/plus4/shppt-plus4.bin
Normal file
1
reference-binaries/plus4/shppt-plus4.bin
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
†6Ð¥CÉŽ° \¢¥Ci \¢©(…(Lxª©÷%(…(Lxª…l…n¥5…ki<03>©ÿ…mL@r
|
||||||
428
src/elite-flicker-free-plus4.asm
Normal file
428
src/elite-flicker-free-plus4.asm
Normal file
|
|
@ -0,0 +1,428 @@
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ BBC MASTER ELITE GAME SOURCE (FLICKER-FREE ROUTINES)
|
||||||
|
\
|
||||||
|
\ BBC Master Elite was written by Ian Bell and David Braben and is copyright
|
||||||
|
\ Acornsoft 1986
|
||||||
|
\
|
||||||
|
\ The code on this site has been reconstructed from a disassembly of the version
|
||||||
|
\ released on Ian Bell's personal website at http://www.elitehomepage.org/
|
||||||
|
\
|
||||||
|
\ The commentary is copyright Mark Moxon, and any misunderstandings or mistakes
|
||||||
|
\ in the documentation are entirely my fault
|
||||||
|
\
|
||||||
|
\ The terminology and notations used in this commentary are explained at
|
||||||
|
\ https://www.bbcelite.com/about_site/terminology_used_in_this_commentary.html
|
||||||
|
\
|
||||||
|
\ The deep dive articles referred to in this commentary can be found at
|
||||||
|
\ https://www.bbcelite.com/deep_dives
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
GUARD &CE00 \ Guard against assembling over memory used by game
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Configuration variables
|
||||||
|
\
|
||||||
|
\ The addresses in the following are from when the game binary is loaded into
|
||||||
|
\ memory. They were calculated by analysing a memory dump of the running game,
|
||||||
|
\ searching for patterns in the bytes to match them with the corrsponding code
|
||||||
|
\ from the BBC Micro version (which is very similar, if you ignore any different
|
||||||
|
\ addresses).
|
||||||
|
\
|
||||||
|
\ XX14 is an unused variable in BBC Micro Elite, and the corresponding address
|
||||||
|
\ in Commodore 64 Elite is used by something else. Luckily locations $FB to $FE
|
||||||
|
\ are unused by Elite and the OS, so we can sneak XX14 in there instead.
|
||||||
|
\
|
||||||
|
\ Also, the Y variable contains the height of the space view in pixels, divided
|
||||||
|
\ by 2. This is 96 on the BBC Micro, but the space view is 144 pixels on the
|
||||||
|
\ Commodore 64, so Y needs to be set to 72 instead.
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
XX1 = $0009
|
||||||
|
INWK = $0009
|
||||||
|
XX19 = $002A
|
||||||
|
CNT = $0030
|
||||||
|
K3 = $0035
|
||||||
|
K4 = $0043
|
||||||
|
XX0 = $0057
|
||||||
|
V = $005B
|
||||||
|
XX15 = $006B
|
||||||
|
X1 = $006B
|
||||||
|
Y1 = $006C
|
||||||
|
X2 = $006D
|
||||||
|
Y2 = $006E
|
||||||
|
XX12 = $0071
|
||||||
|
XX17 = $009F
|
||||||
|
XX4 = $00AD
|
||||||
|
XX20 = $00AE
|
||||||
|
XX14 = $00FB
|
||||||
|
PROJ = $860F \ Note, this is not equal to $7D1F + $0900
|
||||||
|
LL75 = $9FB8 + $0900
|
||||||
|
LL30 = $B49D \ Note, this is not equal to $AB91 + $0900
|
||||||
|
Y = 72
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: SHPPT
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing ships
|
||||||
|
\ Summary: Draw a distant ship as a point rather than a full wireframe
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
ORG $9932 + $0900
|
||||||
|
|
||||||
|
.SHPPT
|
||||||
|
|
||||||
|
JSR PROJ \ Project the ship onto the screen, returning:
|
||||||
|
\
|
||||||
|
\ * K3(1 0) = the screen x-coordinate
|
||||||
|
\ * K4(1 0) = the screen y-coordinate
|
||||||
|
\ * A = K4+1
|
||||||
|
|
||||||
|
ORA K3+1 \ If either of the high bytes of the screen coordinates
|
||||||
|
BNE nono \ are non-zero, jump to nono as the ship is off-screen
|
||||||
|
|
||||||
|
LDA K4 \ Set A = the y-coordinate of the dot
|
||||||
|
|
||||||
|
CMP #Y*2-2 \ If the y-coordinate is bigger than the y-coordinate of
|
||||||
|
BCS nono \ the bottom of the screen, jump to nono as the ship's
|
||||||
|
\ dot is off the bottom of the space view
|
||||||
|
|
||||||
|
JSR Shpt \ Call Shpt to draw a horizontal 4-pixel dash for the
|
||||||
|
\ first row of the dot (i.e. a four-pixel dash)
|
||||||
|
|
||||||
|
LDA K4 \ Set A = y-coordinate of dot + 1 (so this is the second
|
||||||
|
CLC \ row of the two-pixel-high dot)
|
||||||
|
ADC #1
|
||||||
|
|
||||||
|
JSR Shpt \ Call Shpt to draw a horizontal 4-pixel dash for the
|
||||||
|
\ first row of the dot (i.e. a four-pixel dash)
|
||||||
|
|
||||||
|
LDA #%00001000 \ Set bit 3 of the ship's byte #31 to record that we
|
||||||
|
ORA XX1+31 \ have now drawn something on-screen for this ship
|
||||||
|
STA XX1+31
|
||||||
|
|
||||||
|
JMP LL155 \ Jump to LL155 to draw any remaining lines that are
|
||||||
|
\ still in the ship line heap and return from the
|
||||||
|
\ subroutine using a tail call
|
||||||
|
|
||||||
|
.nono
|
||||||
|
|
||||||
|
LDA #%11110111 \ Clear bit 3 of the ship's byte #31 to record that
|
||||||
|
AND XX1+31 \ nothing is being drawn on-screen for this ship
|
||||||
|
STA XX1+31
|
||||||
|
|
||||||
|
JMP LL155 \ Jump to LL155 to draw any remaining lines that are
|
||||||
|
\ still in the ship line heap and return from the
|
||||||
|
\ subroutine using a tail call
|
||||||
|
|
||||||
|
.Shpt
|
||||||
|
|
||||||
|
\ This routine draws a horizontal 4-pixel dash, for
|
||||||
|
\ either the top or the bottom of the ship's dot
|
||||||
|
|
||||||
|
STA Y1 \ Store A in both y-coordinates, as this is a horizontal
|
||||||
|
STA Y2 \ dash at y-coordinate A
|
||||||
|
|
||||||
|
LDA K3 \ Set A = screen x-coordinate of the ship dot
|
||||||
|
|
||||||
|
STA X1 \ Store the x-coordinate of the ship dot in X1, as this
|
||||||
|
\ is where the dash starts
|
||||||
|
|
||||||
|
CLC \ Set A = screen x-coordinate of the ship dot + 3
|
||||||
|
ADC #3
|
||||||
|
|
||||||
|
BCC P%+4 \ If the addition overflowed, set A = 255, the
|
||||||
|
LDA #255 \ x-coordinate of the right edge of the screen
|
||||||
|
|
||||||
|
STA X2 \ Store the x-coordinate of the ship dot in X1, as this
|
||||||
|
\ is where the dash starts
|
||||||
|
|
||||||
|
JMP LLX30 \ Draw this edge using flicker-free animation, by first
|
||||||
|
\ drawing the ship's new line and then erasing the
|
||||||
|
\ corresponding old line from the screen, and return
|
||||||
|
\ from the subroutine using a tail call
|
||||||
|
|
||||||
|
SAVE "shppt-plus4.bin", SHPPT, P%
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: LL9 (Part 11 of 12)
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing ships
|
||||||
|
\ Summary: Draw ship: Loop back for the next edge
|
||||||
|
\ Deep dive: Drawing ships
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
ORG $A15B + $0900
|
||||||
|
|
||||||
|
.LL78
|
||||||
|
|
||||||
|
LDA XX14 \ If XX14 >= CNT, skip to LL81 so we don't loop back for
|
||||||
|
CMP CNT \ the next edge (CNT was set to the maximum heap size
|
||||||
|
BCS LL81 \ for this ship in part 10, so this checks whether we
|
||||||
|
\ have just run out of space in the ship line heap, and
|
||||||
|
\ stops drawing edges if we have)
|
||||||
|
|
||||||
|
LDA V \ Increment V by 4 so V(1 0) points to the data for the
|
||||||
|
CLC \ next edge
|
||||||
|
ADC #4
|
||||||
|
STA V
|
||||||
|
|
||||||
|
BCC ll81 \ If the above addition didn't overflow, jump to ll81
|
||||||
|
|
||||||
|
INC V+1 \ Otherwise increment the high byte of V(1 0), as we
|
||||||
|
\ just moved the V(1 0) pointer past a page boundary
|
||||||
|
|
||||||
|
.ll81
|
||||||
|
|
||||||
|
INC XX17 \ Increment the edge counter to point to the next edge
|
||||||
|
|
||||||
|
LDY XX17 \ If Y >= XX20, which contains the number of edges in
|
||||||
|
CPY XX20 \ the blueprint, skip the following
|
||||||
|
BCS P%+5
|
||||||
|
|
||||||
|
JMP LL75-2 \ Loop back to LL75-2 to process the next edge (we jump
|
||||||
|
\ to LL75-2 as we have modified the code around LL75,
|
||||||
|
\ which moves the jump point back by two bytes)
|
||||||
|
|
||||||
|
.LL81
|
||||||
|
|
||||||
|
NOP
|
||||||
|
|
||||||
|
SAVE "ll78-plus4.bin", LL78, P%
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: LL9 (Part 12 of 12)
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing ships
|
||||||
|
\ Summary: Draw ship: Draw all the visible edges from the ship line heap
|
||||||
|
\ Deep dive: Drawing ships
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
ORG $A178 + $0900
|
||||||
|
|
||||||
|
.LL155
|
||||||
|
|
||||||
|
LDY XX14 \ Set Y to the offset in the line heap XX14
|
||||||
|
|
||||||
|
.LL27
|
||||||
|
|
||||||
|
CPY XX14+1 \ If Y >= XX14+1, jump to LLEX to return from the ship
|
||||||
|
BCS LLEX \ drawing routine, because the index in Y is greater
|
||||||
|
\ than the size of the existing ship line heap, which
|
||||||
|
\ means we have alrady erased all the old ships lines
|
||||||
|
\ when drawing the new ship
|
||||||
|
|
||||||
|
\ If we get here then Y < XX14+1, which means Y is
|
||||||
|
\ pointing to an on-screen line from the old ship that
|
||||||
|
\ we need to erase
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Fetch the X1 line coordinate from the heap and store
|
||||||
|
STA XX15 \ it in XX15
|
||||||
|
|
||||||
|
INY \ Increment the heap pointer
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Fetch the Y1 line coordinate from the heap and store
|
||||||
|
STA XX15+1 \ it in XX15+1
|
||||||
|
|
||||||
|
INY \ Increment the heap pointer
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Fetch the X2 line coordinate from the heap and store
|
||||||
|
STA XX15+2 \ it in XX15+2
|
||||||
|
|
||||||
|
INY \ Increment the heap pointer
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Fetch the Y2 line coordinate from the heap and store
|
||||||
|
STA XX15+3 \ it in XX15+3
|
||||||
|
|
||||||
|
JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2) to erase it from
|
||||||
|
\ the screen
|
||||||
|
|
||||||
|
INY \ Increment the heap pointer
|
||||||
|
|
||||||
|
JMP LL27 \ Loop back to LL27 to draw (i.e. erase) the next line
|
||||||
|
\ from the heap
|
||||||
|
|
||||||
|
.LLEX
|
||||||
|
|
||||||
|
LDA XX14 \ Store XX14 in the first byte of the ship line heap
|
||||||
|
LDY #0
|
||||||
|
STA (XX19),Y
|
||||||
|
|
||||||
|
.LL82
|
||||||
|
|
||||||
|
RTS \ Return from the subroutine
|
||||||
|
|
||||||
|
SAVE "ll155-plus4.bin", LL155, P%
|
||||||
|
|
||||||
|
ORG $7240
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: LLX30
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing lines
|
||||||
|
\ Summary: Draw a ship line using flicker-free animation
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
.LLX30
|
||||||
|
|
||||||
|
LDY XX14 \ Set Y = XX14, to get the offset within the ship line
|
||||||
|
\ heap where we want to insert our new line
|
||||||
|
|
||||||
|
CPY XX14+1 \ Compare XX14 and XX14+1 and store the flags on the
|
||||||
|
PHP \ stack so we can retrieve them later
|
||||||
|
|
||||||
|
LDX #3 \ We now want to copy the line coordinates (X1, Y1) and
|
||||||
|
\ (X2, Y2) to XX12...XX12+3, so set a counter to copy
|
||||||
|
\ 4 bytes
|
||||||
|
|
||||||
|
.LLXL
|
||||||
|
|
||||||
|
LDA X1,X \ Copy the X-th byte of X1/Y1/X2/Y2 to the X-th byte of
|
||||||
|
STA XX12,X \ XX12
|
||||||
|
|
||||||
|
DEX \ Decrement the loop counter
|
||||||
|
|
||||||
|
BPL LLXL \ Loop back until we have copied all four bytes
|
||||||
|
|
||||||
|
JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2)
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Set X1 to the Y-th coordinate on the ship line heap,
|
||||||
|
STA X1 \ i.e. one we are replacing in the heap
|
||||||
|
|
||||||
|
LDA XX12 \ Replace it with the X1 coordinate in XX12
|
||||||
|
STA (XX19),Y
|
||||||
|
|
||||||
|
INY \ Increment the index to point to the Y1 coordinate
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Set Y1 to the Y-th coordinate on the ship line heap,
|
||||||
|
STA Y1 \ i.e. one we are replacing in the heap
|
||||||
|
|
||||||
|
LDA XX12+1 \ Replace it with the Y1 coordinate in XX12+1
|
||||||
|
STA (XX19),Y
|
||||||
|
|
||||||
|
INY \ Increment the index to point to the X2 coordinate
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Set X1 to the Y-th coordinate on the ship line heap,
|
||||||
|
STA X2
|
||||||
|
|
||||||
|
LDA XX12+2 \ Replace it with the X2 coordinate in XX12+2
|
||||||
|
STA (XX19),Y
|
||||||
|
|
||||||
|
INY \ Increment the index to point to the Y2 coordinate
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Set Y2 to the Y-th coordinate on the ship line heap,
|
||||||
|
STA Y2
|
||||||
|
|
||||||
|
LDA XX12+3 \ Replace it with the Y2 coordinate in XX12+3
|
||||||
|
STA (XX19),Y
|
||||||
|
|
||||||
|
INY \ Increment the index to point to the next coordinate
|
||||||
|
STY XX14 \ and store the updated index in XX14
|
||||||
|
|
||||||
|
PLP \ Restore the result of the comparison above, so if the
|
||||||
|
BCS LL82a \ original value of XX14 >= XX14+1, then we have already
|
||||||
|
\ redrawn all the lines from the old ship's line heap,
|
||||||
|
\ so return from the subroutine (as LL82 contains an
|
||||||
|
\ RTS)
|
||||||
|
|
||||||
|
JMP LL30 \ Otherwise there are still more lines to erase from the
|
||||||
|
\ old ship on-screen, so the coordinates in (X1, Y1) and
|
||||||
|
\ (X2, Y2) that we just pulled from the ship line heap
|
||||||
|
\ point to a line that is still on-screen, so call LL30
|
||||||
|
\ to draw this line and erase it from the screen,
|
||||||
|
\ returning from the subroutine using a tail call
|
||||||
|
|
||||||
|
.LL82a
|
||||||
|
|
||||||
|
RTS \ Return from the subroutine
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: LL9 (Part 1 of 12)
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing ships
|
||||||
|
\ Summary: Draw ship: Check if ship is exploding, check if ship is in front
|
||||||
|
\ Deep dive: Drawing ships
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
.PATCH1
|
||||||
|
|
||||||
|
\ We replace the following two instructions in part 1 of
|
||||||
|
\ LL9 with JSR PATCH1, so we start with those two
|
||||||
|
\ instructions to ensure that they still get done
|
||||||
|
|
||||||
|
LDA #31 \ Set XX4 = 31 to store the ship's distance for later
|
||||||
|
STA XX4 \ comparison with the visibility distance. We will
|
||||||
|
\ update this value below with the actual ship's
|
||||||
|
\ distance if it turns out to be visible on-screen
|
||||||
|
|
||||||
|
\ We now set things up for flicker-free ship plotting,
|
||||||
|
\ by setting the following:
|
||||||
|
\
|
||||||
|
\ XX14 = offset to the first coordinate in the ship's
|
||||||
|
\ line heap
|
||||||
|
\
|
||||||
|
\ XX14+1 = the number of bytes in the heap for the
|
||||||
|
\ ship that's currently on-screen (or 0 if
|
||||||
|
\ there is no ship currently on-screen)
|
||||||
|
|
||||||
|
LDY #1 \ Set XX14 = 1, the offset of the first set of line
|
||||||
|
STY XX14 \ coordinates in the ship line heap
|
||||||
|
|
||||||
|
DEY \ Decrement Y to 0
|
||||||
|
|
||||||
|
LDA #%00001000 \ If bit 3 of the ship's byte #31 is set, then the ship
|
||||||
|
BIT INWK+31 \ is currently being drawn on-screen, so skip the
|
||||||
|
BNE P%+5 \ following two instructions
|
||||||
|
|
||||||
|
LDA #0 \ The ship is not being drawn on screen, so set A = 0
|
||||||
|
\ so that XX14+1 gets set to 0 below (as there are no
|
||||||
|
\ existing coordinates on the ship line heap for this
|
||||||
|
\ ship)
|
||||||
|
|
||||||
|
EQUB $2C \ Skip the next instruction by turning it into
|
||||||
|
\ $2C $B1 $BD, or BIT $BDB1 which does nothing apart
|
||||||
|
\ from affect the flags
|
||||||
|
|
||||||
|
LDA (XX19),Y \ Set XX14+1 to the first byte of the ship's line heap,
|
||||||
|
STA XX14+1 \ which contains the number of bytes in the heap
|
||||||
|
|
||||||
|
RTS
|
||||||
|
|
||||||
|
\ ******************************************************************************
|
||||||
|
\
|
||||||
|
\ Name: LL9 (Part 10 of 12)
|
||||||
|
\ Type: Subroutine
|
||||||
|
\ Category: Drawing ships
|
||||||
|
\ Summary: Draw ship: Check if ship is exploding, check if ship is in front
|
||||||
|
\ Deep dive: Drawing ships
|
||||||
|
\
|
||||||
|
\ ******************************************************************************
|
||||||
|
|
||||||
|
.PATCH2
|
||||||
|
|
||||||
|
\ We replace the JMP LL78 instruction at the end of part
|
||||||
|
\ 10 of LL9 with JSR PATCH2, so this effectively inserts
|
||||||
|
\ the call to LLX30 at the end of part 10, as required
|
||||||
|
|
||||||
|
JSR LLX30 \ Draw the laser line using flicker-free animation, by
|
||||||
|
\ first drawing the new laser line and then erasing the
|
||||||
|
\ corresponding old line from the screen
|
||||||
|
|
||||||
|
JMP LL78 \ Jump down to part 11
|
||||||
|
|
||||||
|
|
||||||
|
SAVE "extra-plus4.bin", LLX30, P%
|
||||||
|
|
||||||
|
|
@ -61,7 +61,6 @@ XX20 = $00AE
|
||||||
XX14 = $00FB
|
XX14 = $00FB
|
||||||
PROJ = $7D1F
|
PROJ = $7D1F
|
||||||
LL75 = $9FB8
|
LL75 = $9FB8
|
||||||
LL80 = $A13F
|
|
||||||
LL30 = $AB91
|
LL30 = $AB91
|
||||||
Y = 72
|
Y = 72
|
||||||
|
|
||||||
|
|
|
||||||
321
src/elite-modify-plus4.py
Normal file
321
src/elite-modify-plus4.py
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# ******************************************************************************
|
||||||
|
#
|
||||||
|
# COMMODORE PLUS/4 ELITE FLICKER-FREE MODIFICATION SCRIPT
|
||||||
|
#
|
||||||
|
# Written by Mark Moxon
|
||||||
|
#
|
||||||
|
# This script applies flicker-free ship-drawing to Commodore Plus/4} Elite, as
|
||||||
|
# described here:
|
||||||
|
#
|
||||||
|
# https://www.bbcelite.com/deep_dives/flicker-free_ship_drawing.html
|
||||||
|
#
|
||||||
|
# It does the following:
|
||||||
|
#
|
||||||
|
# * Modify the PRG file to draw flicker-free ships
|
||||||
|
#
|
||||||
|
# Run this script by changing directory to the folder containing the disk files
|
||||||
|
# and running the script with "python elite-modify.py"
|
||||||
|
#
|
||||||
|
# This modification script works with the following disk image, which is the
|
||||||
|
# Pigmy Plus/4 version from Ian Bell's site, with the demo and packing removed:
|
||||||
|
#
|
||||||
|
# * elite_+4_unpacked.prg
|
||||||
|
#
|
||||||
|
# ******************************************************************************
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# Convert a C64 address into the corresponding offset within the gma6 file
|
||||||
|
|
||||||
|
def get_offset(addr):
|
||||||
|
return addr - load_address
|
||||||
|
|
||||||
|
|
||||||
|
# Insert a binary file into the game code, overwriting what's there
|
||||||
|
|
||||||
|
def insert_binary_file(data_block, addr, filename):
|
||||||
|
file = open(filename, "rb")
|
||||||
|
file_size = os.path.getsize(filename)
|
||||||
|
insert_from = get_offset(addr)
|
||||||
|
insert_to = insert_from + file_size
|
||||||
|
data_block[insert_from:insert_to] = file.read()
|
||||||
|
file.close()
|
||||||
|
print("[ Modify ] insert file {} at 0x{:02X}".format(filename, addr))
|
||||||
|
|
||||||
|
|
||||||
|
# Insert an array of bytes into the game code, overwriting what's there
|
||||||
|
|
||||||
|
def insert_bytes(data_block, addr, insert):
|
||||||
|
insert_from = get_offset(addr)
|
||||||
|
insert_to = insert_from + len(insert)
|
||||||
|
data_block[insert_from:insert_to] = insert
|
||||||
|
print("[ Modify ] insert {} bytes at 0x{:02X}".format(len(insert), addr))
|
||||||
|
|
||||||
|
|
||||||
|
# Insert a block of NOPs into the game code, overwriting what's there
|
||||||
|
|
||||||
|
def insert_nops(data_block, addr, count):
|
||||||
|
insert = [0xEA] * count
|
||||||
|
insert_bytes(data_block, addr, insert)
|
||||||
|
print("[ Modify ] insert {} NOPs at 0x{:02X}".format(count, addr))
|
||||||
|
|
||||||
|
|
||||||
|
# Print a progess message
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("Modifying Commodore Plus/4 Elite")
|
||||||
|
|
||||||
|
# Configuration variables
|
||||||
|
|
||||||
|
load_address = 0x1100 - 2
|
||||||
|
|
||||||
|
# Set up an array to hold the game binary, so we can modify it
|
||||||
|
|
||||||
|
data_block = bytearray()
|
||||||
|
|
||||||
|
# Load the main code file into data_block
|
||||||
|
|
||||||
|
elite_file = open("elite_+4_unpacked.prg", "rb")
|
||||||
|
data_block.extend(elite_file.read())
|
||||||
|
elite_file.close()
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("[ Read ] elite_+4_unpacked.prg")
|
||||||
|
|
||||||
|
# Set the addresses for the extra routines (LLX30, PATCH1, PATCH2) that we will
|
||||||
|
# load into unused portiong of the main game code
|
||||||
|
|
||||||
|
llx30 = 0x7240
|
||||||
|
patch1 = 0x727E
|
||||||
|
patch2 = 0x7295
|
||||||
|
|
||||||
|
# We now modify the code to implement flicker-free ship drawing. The code
|
||||||
|
# changes are described here, which can be read alongside the following:
|
||||||
|
#
|
||||||
|
# https://www.bbcelite.com/deep_dives/backporting_the_flicker-free_algorithm.html
|
||||||
|
#
|
||||||
|
# The addresses in the following are from when the game binary is loaded into
|
||||||
|
# memory. They were calculated by analysing a memory dump of the running game,
|
||||||
|
# searching for patterns in the bytes to match them with the corrsponding code
|
||||||
|
# from the BBC Micro version (which is very similar, if you ignore any different
|
||||||
|
# addresses).
|
||||||
|
|
||||||
|
# SHPPT
|
||||||
|
#
|
||||||
|
# We start with the new version of SHPPT, which we have already assembled in
|
||||||
|
# BeebAsm and saved as the binary file shppt.bin, so we simply drop this over
|
||||||
|
# the top of the existing routine (which is slightly longer, so there is room).
|
||||||
|
|
||||||
|
insert_binary_file(data_block, 0x9932 + 0x900, "shppt-plus4.bin")
|
||||||
|
|
||||||
|
# LL9 (Part 1)
|
||||||
|
#
|
||||||
|
# This is the modification just after LL9. We insert the extra code with a call
|
||||||
|
# to the new PATCH1 routine, which implements the original instructions before
|
||||||
|
# moving on to the new code.
|
||||||
|
#
|
||||||
|
# From: LDA #31
|
||||||
|
# STA XX4
|
||||||
|
#
|
||||||
|
# To: JSR PATCH1
|
||||||
|
# NOP
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9A8A + 0x900, [
|
||||||
|
0x20, patch1 % 256, patch1 // 256 # JSR PATCH1
|
||||||
|
])
|
||||||
|
insert_nops(data_block, 0x9A8D + 0x900, 1)
|
||||||
|
|
||||||
|
# LL9 (Part 9)
|
||||||
|
#
|
||||||
|
# This is the modification at EE31.
|
||||||
|
#
|
||||||
|
# From: LDA #%00001000
|
||||||
|
# BIT XX1+31
|
||||||
|
# BEQ LL74
|
||||||
|
# JSR LL155
|
||||||
|
#
|
||||||
|
# To: LDY #9
|
||||||
|
# LDA (XX0),Y
|
||||||
|
# STA XX20
|
||||||
|
# NOP
|
||||||
|
# NOP
|
||||||
|
# NOP
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9F2A + 0x900, [
|
||||||
|
0xA0, 0x09, # LDY #9
|
||||||
|
0xB1, 0x57, # LDA (XX0),Y
|
||||||
|
0x85, 0xAE # STA XX20
|
||||||
|
])
|
||||||
|
insert_nops(data_block, 0x9F30 + 0x900, 3)
|
||||||
|
|
||||||
|
# LL9 (Part 9)
|
||||||
|
#
|
||||||
|
# This is the modification just after LL74.
|
||||||
|
#
|
||||||
|
# From: LDY #9
|
||||||
|
# LDA (XX0),Y
|
||||||
|
# STA XX20
|
||||||
|
# LDY #0
|
||||||
|
# STY U
|
||||||
|
# STY XX17
|
||||||
|
# INC U
|
||||||
|
#
|
||||||
|
# To: LDY #0
|
||||||
|
# STY XX17
|
||||||
|
# NOP x10
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9F39 + 0x900, [
|
||||||
|
0xA0, 0x00, # LDY #0
|
||||||
|
0x84, 0x9F # STY XX17
|
||||||
|
])
|
||||||
|
insert_nops(data_block, 0x9F3D + 0x900, 10)
|
||||||
|
|
||||||
|
# LL9 (Part 9)
|
||||||
|
#
|
||||||
|
# This is the modification at the end of the routine.
|
||||||
|
#
|
||||||
|
# From: LDA XX15
|
||||||
|
# STA (XX19),Y
|
||||||
|
# INY
|
||||||
|
# LDA XX15+1
|
||||||
|
# STA (XX19),Y
|
||||||
|
# INY :
|
||||||
|
# LDA XX15+2
|
||||||
|
# STA (XX19),Y
|
||||||
|
# INY
|
||||||
|
# LDA XX15+3
|
||||||
|
# STA (XX19),Y
|
||||||
|
# INY
|
||||||
|
# STY U
|
||||||
|
#
|
||||||
|
# To: JSR LLX30
|
||||||
|
# NOP x21
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9F87 + 0x900, [
|
||||||
|
0x20, llx30 % 256, llx30 // 256 # JSR LLX30
|
||||||
|
])
|
||||||
|
insert_nops(data_block, 0x9F8A + 0x900, 21)
|
||||||
|
|
||||||
|
# LL9 (Part 10)
|
||||||
|
#
|
||||||
|
# This is the modification around LL75.
|
||||||
|
#
|
||||||
|
# From: STA T1
|
||||||
|
# LDY XX17
|
||||||
|
#
|
||||||
|
# To: STA CNT
|
||||||
|
# LDY #0
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9FB4 + 0x900, [
|
||||||
|
0x85, 0x30, # STA CNT
|
||||||
|
0xA0, 0x00 # LDY #0
|
||||||
|
])
|
||||||
|
|
||||||
|
# LL9 (Part 10)
|
||||||
|
#
|
||||||
|
# This is the second INY after LL75.
|
||||||
|
#
|
||||||
|
# From: INY
|
||||||
|
#
|
||||||
|
# To: NOP
|
||||||
|
|
||||||
|
insert_nops(data_block, 0x9FC1 + 0x900, 1)
|
||||||
|
|
||||||
|
# LL9 (Part 10)
|
||||||
|
#
|
||||||
|
# This are the two modifications at LL79.
|
||||||
|
#
|
||||||
|
# From: LDA (V),Y
|
||||||
|
# TAX
|
||||||
|
# INY
|
||||||
|
# LDA (V),Y
|
||||||
|
# STA Q
|
||||||
|
# ... four lots of unchanged LDA/STA, 5 bytes each ...
|
||||||
|
# LDX Q
|
||||||
|
#
|
||||||
|
# To: INY
|
||||||
|
# LDA (V),Y
|
||||||
|
# TAX
|
||||||
|
# ... shuffle the LDA/STA block down by 4 bytes ...
|
||||||
|
# INY
|
||||||
|
# LDA (V),Y
|
||||||
|
# TAX
|
||||||
|
# NOP
|
||||||
|
# NOP
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9FD9 + 0x900, [
|
||||||
|
0xC8, # INY
|
||||||
|
0xB1, 0x5B, # LDA (V),Y
|
||||||
|
0xAA # TAX
|
||||||
|
])
|
||||||
|
|
||||||
|
lda_sta_block = get_offset(0x9FDD + 0x900)
|
||||||
|
for n in range(lda_sta_block, lda_sta_block + 4 * 5):
|
||||||
|
data_block[n] = data_block[n + 4]
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0x9FF1 + 0x900, [
|
||||||
|
0xC8, # INY
|
||||||
|
0xB1, 0x5B, # LDA (V),Y
|
||||||
|
0xAA # TAX
|
||||||
|
])
|
||||||
|
insert_nops(data_block, 0x9FF5 + 0x900, 2)
|
||||||
|
|
||||||
|
# LL9 (Part 10)
|
||||||
|
#
|
||||||
|
# This is the modification at the end of the routine. The C64 version has an
|
||||||
|
# extra JMP LL80 instruction at this point that we can modify to jump to a
|
||||||
|
# new routine PATCH2, which lets us insert the extra JSR LLX30 without taking
|
||||||
|
# up any more bytes.
|
||||||
|
#
|
||||||
|
# From: JMP LL80
|
||||||
|
#
|
||||||
|
# To: JMP PATCH2
|
||||||
|
|
||||||
|
insert_bytes(data_block, 0xA010 + 0x900, [
|
||||||
|
0x4C, patch2 % 256, patch2 // 256 # JMP PATCH2
|
||||||
|
])
|
||||||
|
|
||||||
|
# LL9 (Part 11)
|
||||||
|
#
|
||||||
|
# This is the modification at LL80.
|
||||||
|
#
|
||||||
|
# We blank out the .LL80 section with 28 NOPs
|
||||||
|
|
||||||
|
insert_nops(data_block, 0xA13F + 0x900, 28)
|
||||||
|
|
||||||
|
# LL9 (Part 11)
|
||||||
|
#
|
||||||
|
# We have already assembled the modified part 11 in BeebAsm and saved it as
|
||||||
|
# the binary file ll78.bin, so now we drop this over the top of the existing
|
||||||
|
# routine (which is exactly the same size).
|
||||||
|
|
||||||
|
insert_binary_file(data_block, 0xA15B + 0x900, "ll78-plus4.bin")
|
||||||
|
|
||||||
|
# LL9 (Part 12)
|
||||||
|
#
|
||||||
|
# We have already assembled the modified part 11 in BeebAsm and saved it as
|
||||||
|
# the binary file ll115.bin, so now we drop this over the top of the existing
|
||||||
|
# routine (which is slightly longer, so there is room).
|
||||||
|
|
||||||
|
insert_binary_file(data_block, 0xA178 + 0x900, "ll155-plus4.bin")
|
||||||
|
|
||||||
|
# We now load the three extra routines required by the modifications into the
|
||||||
|
# memory used by the Trumble sprites, which are not used in the Plus/4 version:
|
||||||
|
#
|
||||||
|
# LLX30
|
||||||
|
# PATCH1
|
||||||
|
# PATCH2
|
||||||
|
|
||||||
|
insert_binary_file(data_block, 0x7240, "extra-plus4.bin")
|
||||||
|
|
||||||
|
# All the modifications are done, so write the output file for the modified PRG
|
||||||
|
|
||||||
|
output_file = open("elite_+4_modified.prg", "wb")
|
||||||
|
output_file.write(data_block)
|
||||||
|
output_file.close()
|
||||||
|
|
||||||
|
print("[ Save ] elite_+4_modified.prg")
|
||||||
Loading…
Reference in a new issue