Added patched version for Commodore Plus/4

This commit is contained in:
Mark Moxon 2022-09-06 10:25:23 +01:00
commit f5a52fcab5
13 changed files with 789 additions and 4 deletions

View file

@ -26,6 +26,7 @@ https://user-images.githubusercontent.com/2428251/187879166-74e973e3-aa49-40e3-b
* [A better algorithm](#a-better-algorithm)
* [The patching process](#the-patching-process)
* [Patching the Commodore Plus/4 version][#patching-the-commodore-plus-4-version]
* [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 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.
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).
### 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.
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.
@ -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).
### 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
### Requirements

View file

@ -39,7 +39,7 @@ $c1541 \
cd ..
$python src/crc32.py reference-binaries/ntsc work
# And now, we build the PAL version
# Next, we build the PAL version
cd work
# Extract the files from the original disk image
@ -69,3 +69,19 @@ $c1541 \
# Report checksums
cd ..
$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

Binary file not shown.

BIN
original-disks/ELITEPIG.PRG Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
¥ûÅ0°¥[i…[<5B>æ\柤ŸÄ®°L¶¨ê

View file

@ -0,0 +1 @@
¥CÉŽ° \¢¥Ci \¢©(…(Lxª©÷%(…(Lxª…l…n¥5…ki<03>©ÿ…mL@r

View 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%

View file

@ -61,7 +61,6 @@ XX20 = $00AE
XX14 = $00FB
PROJ = $7D1F
LL75 = $9FB8
LL80 = $A13F
LL30 = $AB91
Y = 72

321
src/elite-modify-plus4.py Normal file
View 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")