Showing preview only (2,400K chars total). Download the full file or copy to clipboard to get everything.
Repository: markmoxon/elite-beebasm
Branch: main
Commit: a8bc28d22ed9
Files: 91
Total size: 2.3 MB
Directory structure:
gitextract_xn5smrlz/
├── .gitattributes
├── .gitignore
├── 1-source-files/
│ ├── README.md
│ ├── basic-programs/
│ │ └── README.md
│ ├── boot-files/
│ │ └── README.md
│ ├── images/
│ │ └── README.md
│ ├── main-sources/
│ │ ├── README.md
│ │ ├── elite-bcfs.asm
│ │ ├── elite-build-options.asm
│ │ ├── elite-disc.asm
│ │ ├── elite-loader.asm
│ │ ├── elite-readme.asm
│ │ └── elite-source.asm
│ └── original-sources/
│ ├── $.DEFEDIT.inf
│ ├── $.DEFEDIT.txt
│ ├── $.DEFGEN.inf
│ ├── $.DEFGEN.txt
│ ├── $.DEFTRAN.inf
│ ├── $.DEFTRAN.txt
│ ├── $.DIALGEN.inf
│ ├── $.DIALGEN.txt
│ ├── $.ELITEA.inf
│ ├── $.ELITEA.txt
│ ├── $.ELITEB.inf
│ ├── $.ELITEB.txt
│ ├── $.ELITEC.inf
│ ├── $.ELITEC.txt
│ ├── $.ELITED.inf
│ ├── $.ELITED.txt
│ ├── $.ELITEE.inf
│ ├── $.ELITEE.txt
│ ├── $.ELITEF.inf
│ ├── $.ELITEF.txt
│ ├── $.ELITEG.inf
│ ├── $.ELITEG.txt
│ ├── $.ELITES.inf
│ ├── $.ELITES.txt
│ ├── $.FINDSC2.inf
│ ├── $.FINDSC2.txt
│ ├── $.GENTOK.inf
│ ├── $.GENTOK.txt
│ ├── $.GETBIT.inf
│ ├── $.GETBIT.txt
│ ├── $.MAKER.inf
│ ├── $.MAKER.txt
│ ├── $.SBLOCK.inf
│ ├── $.SBLOCK.txt
│ ├── $.SFTPROT.inf
│ ├── $.SFTPROT.txt
│ ├── $.SHPPRTE.inf
│ ├── $.SHPPRTE.txt
│ ├── $.TOKPRI.inf
│ ├── $.TOKPRI.txt
│ ├── $.UNPACK.inf
│ ├── $.UNPACK.txt
│ ├── A.GENTOK.inf
│ ├── A.GENTOK.txt
│ ├── O.ELITED.inf
│ ├── O.ELITED.txt
│ ├── README.md
│ ├── S.BCFS.inf
│ ├── S.BCFS.txt
│ ├── S.GENTOK.inf
│ ├── S.GENTOK.txt
│ ├── S.STEST.inf
│ ├── S.STEST.txt
│ └── text-sources/
│ ├── ELITEA.txt
│ ├── ELITEB.txt
│ ├── ELITEC.txt
│ ├── ELITED.txt
│ ├── ELITEE.txt
│ ├── ELITEF.txt
│ └── ELITEG.txt
├── 2-build-files/
│ ├── README.md
│ ├── crc32.py
│ ├── elite-checksum.py
│ ├── elite-decrypt.py
│ ├── mktibet-0.3.php
│ └── tibetuef-0.8.php
├── 3-assembled-output/
│ ├── README.md
│ ├── README.txt
│ └── compile.txt
├── 4-reference-binaries/
│ └── README.md
├── 5-compiled-game-discs/
│ ├── README.md
│ ├── elite-cassette-from-source-disc.ssd
│ ├── elite-cassette-from-text-sources.ssd
│ ├── elite-cassette-sth.ssd
│ └── elite-cassette-sth.uef
├── Makefile
├── README.md
└── make.bat
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# IDE files
.vscode/
*.code-workspace
run.bat
================================================
FILE: 1-source-files/README.md
================================================
# Source files for the BBC Micro cassette version of Elite
This folder contains the source files for the BBC Micro cassette version of Elite.
* [basic-programs](basic-programs) contains any BASIC programs to be included on the final game disc
* [boot-files](boot-files) contains any !BOOT files to be included on the final game disc
* [images](images) contains the image binaries for the title screen and dashboard
* [main-sources](main-sources) contains the annotated source code
* [original-sources](original-sources) contains the original source code from Ian Bell's personal website
---
Right on, Commanders!
_Mark Moxon_
================================================
FILE: 1-source-files/basic-programs/README.md
================================================
# BASIC programs for the BBC Micro cassette version of Elite
This folder contains the BASIC programs from the original sources for the BBC Micro cassette version of Elite on Ian Bell's personal website.
* [$.ELITE.bin]($.ELITE.bin) is the mode 7 loader program for the version that loads from disc, with an added command to insert a key into the keyboard buffer, so the Elite loader doesn't wait for a key press
* [$.ELITE-cassette.bin]($.ELITE-cassette.bin) is the mode 7 loader program for the original cassette version
* [$.ELITE-disc.bin]($.ELITE-disc.bin) is the mode 7 loader program for the version that loads from disc
---
Right on, Commanders!
_Mark Moxon_
================================================
FILE: 1-source-files/boot-files/README.md
================================================
# Boot files for the BBC Micro cassette version of Elite
This folder contains the boot file from the original sources for the BBC Micro cassette version of Elite on Ian Bell's personal website.
* [$.!BOOT.bin]($.!BOOT.bin) is the original boot file from the game disc
---
Right on, Commanders!
_Mark Moxon_
================================================
FILE: 1-source-files/images/README.md
================================================
# Image binaries for the BBC Micro cassette version of Elite
This folder contains the image binaries from the original sources for the BBC Micro cassette version of Elite on Ian Bell's personal website.
* [P.(C)ASFT.bin](P.(C)ASFT.bin) is the "(c) ACORNSOFT 1984" image for the bottom of the title screen
* [P.A-SOFT.bin](P.A-SOFT.bin) is the "ACORNSOFT" image for the very top of the title screen
* [P.DIALS.bin](P.DIALS2P.bin) is the dashboard image
* [P.ELITE.bin](P.ELITE.bin) is the "ELITE" image for the title screen
---
Right on, Commanders!
_Mark Moxon_
================================================
FILE: 1-source-files/main-sources/README.md
================================================
# Annotated source code for the BBC Micro cassette version of Elite
This folder contains the annotated source code for the BBC Micro cassette version of Elite.
* Main source files:
* [elite-source.asm](elite-source.asm) contains the main source for the game
* [elite-bcfs.asm](elite-bcfs.asm) contains the Big Code File source, which concatenates individually assembled binaries into the final game binary
* Other source files:
* [elite-loader.asm](elite-loader.asm) contains the source for the loader
* [elite-disc.asm](elite-disc.asm) builds the SSD disc image from the assembled binaries and other source files
* [elite-readme.asm](elite-readme.asm) generates a README file for inclusion on the SSD disc image
* Files that are generated during the build process:
* [elite-build-options.asm](elite-build-options.asm) stores the make options in BeebAsm format so they can be included in the assembly process
---
Right on, Commanders!
_Mark Moxon_
================================================
FILE: 1-source-files/main-sources/elite-bcfs.asm
================================================
\ ******************************************************************************
\
\ BBC MICRO CASSETTE ELITE BIG CODE FILE SOURCE
\
\ BBC Micro cassette Elite was written by Ian Bell and David Braben and is
\ copyright Acornsoft 1984
\
\ The code in this file is identical to the source discs released on Ian Bell's
\ personal website at http://www.elitehomepage.org/ (it's just been reformatted
\ to be more readable)
\
\ 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://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file contains code to produce the Big Code File for BBC Micro
\ cassette Elite. The Big Code File comprises the game code and the ship
\ blueprints.
\
\ ------------------------------------------------------------------------------
\
\ This source file produces the following binary files:
\
\ * ELTcode.unprot.bin
\ * ELThead.bin
\
\ after reading in the following files:
\
\ * ELTA.bin
\ * ELTB.bin
\ * ELTC.bin
\ * ELTD.bin
\ * ELTE.bin
\ * ELTF.bin
\ * ELTG.bin
\ * SHIPS.bin
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_SOURCE_DISC = (_VARIANT = 1)
_TEXT_SOURCES = (_VARIANT = 2)
_STH_CASSETTE = (_VARIANT = 3)
GUARD &8000 \ Guard against assembling over MOS memory
\ ******************************************************************************
\
\ Configuration variables
\
\ ******************************************************************************
CODE% = &0F40 \ CODE% is set to the location that the main game code
\ gets moved to after it is loaded
LOAD% = &1128 \ LOAD% points to the start of the actual game code,
\ after the &28 bytes of header code that are inserted
\ below
\ ******************************************************************************
\
\ Name: ZP
\ Type: Workspace
\ Address: &0070 to &0071
\ Category: Workspaces
\ Summary: Important variables used by the loader
\
\ ******************************************************************************
ORG &0070 \ Set the assembly address to &0070
.ZP
SKIP 2 \ Stores addresses used for moving content around
\ ******************************************************************************
\
\ Load the compiled binaries to create the Big Code File
\
\ ******************************************************************************
ORG &1100 \ The load address of the main game code file ("ELTcode"
\ for loading from disc, "ELITEcode" for loading from
\ tape)
\ ******************************************************************************
\
\ Name: LBL
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Checksum the two pages of code that were copied from UU% to LE%
\
\ ------------------------------------------------------------------------------
\
\ This routine is called at LBL+1 from the CHECKER routine in the loader code in
\ elite-loader.asm. It calculates the checksum of the first two pages of the
\ loader code that was copied from UU% to LE% by part 3 of the loader, and
\ checks the result against the result in the first byte of the LE% block,
\ CHECKbyt, at address &0B00.
\
\ ------------------------------------------------------------------------------
\
\ Other entry points:
\
\ LBL+2 Contains an RTS
\
\ ******************************************************************************
.LBL
EQUB &6C \ This value is decremented by the tape loading routine
\ in the loader, in IRQ1. During loading this value gets
\ decremented down to &6C, and this new value is then
\ included in the checksum calculation for the MAINSUM
\ checksum in the CHECKER routine (the value is set to
\ &6C here as the tape protection is disabled)
LDX #&60 \ Set X = &60. This value of X isn't used, it's just a
\ set up for the RTS call below, where we jump to LBL+2
\ to perform an RTS, as the opcode for RTS is &60
\ We now run a checksum on the block of memory from
\ &0B01 to &0CFF, which is the UU% routine from the
\ loader
LDA #&B \ Set ZP(1 0) = &0B00, to point to the start of the code
STA ZP+1 \ we want to checksum
LDY #0 \ Set Y = 0 to count through each byte within each page
STY ZP
TYA \ Set A = 0 for building the checksum
INY \ Increment Y to 1
.CHK3
CLC \ Add the Y-th byte of the game code to A
ADC (ZP),Y
INY \ Increment the counter to point to the next byte
BNE CHK3 \ Loop back for the next byte until we have finished
\ adding up this page
INC ZP+1 \ Increment the high byte of ZP(1 0) to point to the
\ next page
.CHK4
CLC \ Add the Y-th byte of this page to the checksum in A
ADC (ZP),Y
INY \ Increment the counter for this page
BPL CHK4 \ Loop back for the next byte until we have finished
\ adding up this second page
CMP &0B00 \ Compare the result to the contents of CHECKbyt in the
\ loader code at elite-loader.asm. This value gets set
\ by elite-checksum.py
BEQ LBL+2 \ If the checksums match, jump to LBL+2, which contains
\ an RTS
\ Otherwise the checksum just failed, so we reset the
\ machine
LDA #%01111111 \ Set 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bits 0-6 (i.e. disable all hardware
\ interrupts from the System VIA)
JMP (&FFFC) \ Jump to the address in &FFFC to reset the machine
.elitea
PRINT "elitea = ", ~P%
INCBIN "3-assembled-output/ELTA.bin"
.eliteb
PRINT "eliteb = ", ~P%
INCBIN "3-assembled-output/ELTB.bin"
.elitec
PRINT "elitec = ", ~P%
INCBIN "3-assembled-output/ELTC.bin"
.elited
PRINT "elited = ", ~P%
INCBIN "3-assembled-output/ELTD.bin"
.elitee
PRINT "elitee = ", ~P%
INCBIN "3-assembled-output/ELTE.bin"
.elitef
PRINT "elitef = ", ~P%
INCBIN "3-assembled-output/ELTF.bin"
.eliteg
PRINT "eliteg = ", ~P%
INCBIN "3-assembled-output/ELTG.bin"
.checksum0
PRINT "checksum0 = ", ~P%
IF _SOURCE_DISC OR _TEXT_SOURCES
SKIP 1 \ We skip this byte so we can insert the checksum later
\ in elite-checksum.py
ELIF _STH_CASSETTE
EQUB &20 \ We skip this byte so we can insert the checksum later
\ in elite-checksum.py; it contains workspace noise in
\ the Stairway to Hell variant
ENDIF
.ships
PRINT "ships = ", ~P%
INCBIN "3-assembled-output/SHIPS.bin"
.end
\ ******************************************************************************
\
\ Save ELTcode.unprot.bin and ELThead.bin
\
\ ******************************************************************************
PRINT "P% = ", ~P%
PRINT "S.ELTcode 1100 ", ~(LOAD% + &6000 - CODE%), " ", ~LOAD%, " ", ~LOAD%
SAVE "3-assembled-output/ELTcode.unprot.bin", &1100, (LOAD% + &6000 - CODE%), LOAD%
SAVE "3-assembled-output/ELThead.bin", &1100, elitea, &1100
================================================
FILE: 1-source-files/main-sources/elite-build-options.asm
================================================
_VERSION=1
_VARIANT=3
_REMOVE_CHECKSUMS=FALSE
_MAX_COMMANDER=FALSE
_DISC=TRUE
_PROT=FALSE
================================================
FILE: 1-source-files/main-sources/elite-disc.asm
================================================
\ ******************************************************************************
\
\ BBC MICRO CASSETTE ELITE DISC IMAGE SCRIPT
\
\ BBC Micro cassette Elite was written by Ian Bell and David Braben and is
\ copyright Acornsoft 1984
\
\ The code in this file is identical to the source discs released on Ian Bell's
\ personal website at http://www.elitehomepage.org/ (it's just been reformatted
\ to be more readable)
\
\ 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://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file produces an SSD disc image for BBC Micro cassette Elite.
\
\ ------------------------------------------------------------------------------
\
\ This source file produces one of the following SSD disc images, depending on
\ which release is being built:
\
\ * elite-cassette-sth.ssd
\ * elite-cassette-from-source-disc.ssd
\ * elite-cassette-from-text-sources.ssd
\
\ This can be loaded into an emulator or a real BBC Micro.
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_SOURCE_DISC = (_VARIANT = 1)
_TEXT_SOURCES = (_VARIANT = 2)
_STH_CASSETTE = (_VARIANT = 3)
IF _DISC
PUTFILE "1-source-files/boot-files/$.!BOOT.bin", "!BOOT", &FFFFFF, &FFFFFF
PUTFILE "1-source-files/basic-programs/$.ELITE.bin", "ELITE", &FF1900, &FF8023
PUTFILE "3-assembled-output/ELITE.bin", "ELTdata", &FF1100, &FF2000
PUTFILE "3-assembled-output/ELTcode.bin", "ELTcode", &FF1128, &FF1128
ELSE
PUTFILE "1-source-files/boot-files/$.!BOOT.bin", "!BOOT", &FFFFFF, &FFFFFF
PUTFILE "1-source-files/basic-programs/$.ELITE-cassette.bin", "ELITE", &FF1900, &FF8023
\PUTFILE "3-assembled-output/ELITE.bin", "ELITEdata", &FF0E00, &FF1D00
\PUTFILE "3-assembled-output/ELTcode.bin", "ELITEcode", &000E00, &000132
PUTFILE "3-assembled-output/ELITE.bin", "ELITEda", &FF0E00, &FF1D00
PUTFILE "3-assembled-output/ELTcode.bin", "ELITEco", &000E00, &000132
ENDIF
PUTFILE "3-assembled-output/README.txt", "README", &FFFFFF, &FFFFFF
================================================
FILE: 1-source-files/main-sources/elite-loader.asm
================================================
\ ******************************************************************************
\
\ BBC MICRO CASSETTE ELITE GAME LOADER SOURCE
\
\ BBC Micro cassette Elite was written by Ian Bell and David Braben and is
\ copyright Acornsoft 1984
\
\ The code in this file is identical to the source discs released on Ian Bell's
\ personal website at http://www.elitehomepage.org/ (it's just been reformatted
\ to be more readable)
\
\ 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://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file contains the game loader for BBC Micro cassette Elite.
\
\ ------------------------------------------------------------------------------
\
\ This source file produces the following binary file:
\
\ * ELITE.unprot.bin
\
\ after reading in the following files:
\
\ * P.A-SOFT.bin
\ * P.(C)ASFT.bin
\ * DIALS.bin
\ * P.ELITE.bin
\ * PYTHON.bin
\ * WORDS9.bin
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_SOURCE_DISC = (_VARIANT = 1)
_TEXT_SOURCES = (_VARIANT = 2)
_STH_CASSETTE = (_VARIANT = 3)
GUARD &6000 \ Guard against assembling over screen memory
\ ******************************************************************************
\
\ Configuration variables
\
\ ******************************************************************************
DISC = _DISC \ Set to TRUE to load the code above DFS and relocate
\ down, so we can load the cassette version from disc,
\ or set to FALSE to load the code as if it were from a
\ cassette
PROT = _PROT \ Set to TRUE to enable the tape protection code, or set
\ to FALSE to disable the code (for TRUE, the file must
\ be saved to tape with the block data corrupted, so you
\ probably want to leave this as FALSE)
IF DISC
CODE% = &1100 \ CODE% is set to the assembly address of the loader
\ code file that we assemble in this file ("ELITE"),
\ which is at the lowest DFS page value of &1100 for the
\ version that loads from disc
LOAD% = &1100 \ LOAD% is the load address of the main game code file
\ ("ELTcode" for loading from disc, "ELITEcode" for
\ loading from tape)
L% = &1128 \ L% points to the start of the actual game code from
\ elite-source.asm, after the &28 bytes of header code
\ that are inserted by elite-bcfs.asm
ELSE
CODE% = &0E00 \ CODE% is set to the assembly address of the loader
\ code file that we assemble in this file ("ELITE"),
\ which is at the standard &0E00 address for the version
\ that loads from cassette
LOAD% = &0F1F \ LOAD% is the load address of the main game code file
\ ("ELTcode" for loading from disc, "ELITEcode" for
\ loading from tape)
L% = &0F47 \ L% points to the start of the actual game code from
\ elite-source.asm, after the &28 bytes of header code
\ that are inserted by elite-bcfs.asm
ENDIF
LEN1 = 15 \ Size of the BEGIN% routine that gets pushed onto the
\ stack and executed there
LEN2 = 18 \ Size of the MVDL routine that gets pushed onto the
\ stack and executed there
LEN = LEN1 + LEN2 \ Total number of bytes that get pushed on the stack for
\ execution there (33)
VSCAN = 57-1 \ Defines the split position in the split-screen mode
LE% = &0B00 \ LE% is the address to which the code from UU% onwards
\ is copied in part 3. It contains:
\
\ * ENTRY2, the entry point for the second block of
\ loader code
\
\ * IRQ1, the interrupt routine for the split-screen
\ mode
\
\ * BLOCK, which by this point has already been put
\ on the stack by this point
\
\ * The variables used by the above
NETV = &0224 \ The NETV vector that we intercept as part of the copy
\ protection
IRQ1V = &0204 \ The IRQ1V vector that we intercept to implement the
\ split-screen mode
OSPRNT = &0234 \ The address for the OSPRNT vector
C% = &0F40 \ C% is set to the location that the main game code gets
\ moved to after it is loaded
S% = C% \ S% points to the entry point for the main game code
IF _SOURCE_DISC
D% = &563A \ D% is set to the address of the byte after the end of
\ the code, i.e. the byte after checksum0 at XX21
ELIF _TEXT_SOURCES
D% = &5638 \ D% is set to the address of the byte after the end of
\ the code, i.e. the byte after checksum0 at XX21
ELIF _STH_CASSETTE
D% = &563A \ D% is set to the address of the byte after the end of
\ the code, i.e. the byte after checksum0 at XX21
ENDIF
LC% = &6000 - C% \ LC% is set to the maximum size of the main game code
\ (as the code starts at C% and screen memory starts
\ at &6000)
N% = 67 \ N% is set to the number of bytes in the VDU table, so
\ we can loop through them in part 2 below
SVN = &7FFD \ SVN is where we store the "saving in progress" flag,
\ and it matches the location in elite-source.asm
VEC = &7FFE \ VEC is where we store the original value of the IRQ1
\ vector, and it matches the value in elite-source.asm
VIA = &FE00 \ Memory-mapped space for accessing internal hardware,
\ such as the video ULA, 6845 CRTC and 6522 VIAs (also
\ known as SHEILA)
OSWRCH = &FFEE \ The address for the OSWRCH routine
OSBYTE = &FFF4 \ The address for the OSBYTE routine
OSWORD = &FFF1 \ The address for the OSWORD routine
\ ******************************************************************************
\
\ Name: ZP
\ Type: Workspace
\ Address: &0004 to &0005 and &0070 to &0086
\ Category: Workspaces
\ Summary: Important variables used by the loader
\
\ ******************************************************************************
ORG &0004 \ Set the assembly address to &0004
.TRTB%
SKIP 2 \ Contains the address of the keyboard translation
\ table, which is used to translate internal key
\ numbers to ASCII
ORG &0070 \ Set the assembly address to &0070
.ZP
SKIP 2 \ Stores addresses used for moving content around
.P
SKIP 1 \ Temporary storage, used in a number of places
.Q
SKIP 1 \ Temporary storage, used in a number of places
.YY
SKIP 1 \ Temporary storage, used in a number of places
.T
SKIP 1 \ Temporary storage, used in a number of places
.SC
SKIP 1 \ Screen address (low byte)
\
\ Elite draws on-screen by poking bytes directly into
\ screen memory, and SC(1 0) is typically set to the
\ address of the character block containing the pixel
\ we want to draw
.SCH
SKIP 1 \ Screen address (high byte)
.BLPTR
SKIP 2 \ Gets set to &03CA as part of the obfuscation code
.V219
SKIP 2 \ Gets set to &0218 as part of the obfuscation code
SKIP 4 \ These bytes appear to be unused
.K3
SKIP 1 \ Temporary storage, used in a number of places
.BLCNT
SKIP 2 \ Stores the tape loader block count as part of the copy
\ protection code in IRQ1
.BLN
SKIP 2 \ Gets set to &03C6 as part of the obfuscation code
.EXCN
SKIP 2 \ Gets set to &03C2 as part of the obfuscation code
\ ******************************************************************************
\
\ ELITE LOADER
\
\ ******************************************************************************
ORG CODE% \ Set the assembly address to CODE%
\ ******************************************************************************
\
\ Name: Elite loader (Part 1 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Include binaries for recursive tokens, Python blueprint and images
\
\ ------------------------------------------------------------------------------
\
\ The loader bundles a number of binary files in with the loader code, and moves
\ them to their correct memory locations in part 3 below.
\
\ There are two files containing code:
\
\ * WORDS9.bin contains the recursive token table, which is moved to &0400
\ before the main game is loaded
\
\ * PYTHON.bin contains the Python ship blueprint, which gets moved to &7F00
\ before the main game is loaded
\
\ and four files containing images, which are all moved into screen memory by
\ the loader:
\
\ * P.A-SOFT.bin contains the "ACORNSOFT" title across the top of the loading
\ screen, which gets moved to screen address &6100, on the second character
\ row of the monochrome mode 4 screen
\
\ * P.ELITE.bin contains the "ELITE" title across the top of the loading
\ screen, which gets moved to screen address &6300, on the fourth character
\ row of the monochrome mode 4 screen
\
\ * P.(C)ASFT.bin contains the "(C) Acornsoft 1984" title across the bottom
\ of the loading screen, which gets moved to screen address &7600, the
\ penultimate character row of the monochrome mode 4 screen, just above the
\ dashboard
\
\ * P.DIALS.bin contains the dashboard, which gets moved to screen address
\ &7800, which is the starting point of the four-colour mode 5 portion at
\ the bottom of the split screen
\
\ The routine ends with a jump to the start of the loader code at ENTRY.
\
\ ******************************************************************************
PRINT "WORDS9 = ", ~P%
INCBIN "3-assembled-output/WORDS9.bin"
ALIGN 256
PRINT "P.DIALS = ", ~P%
INCBIN "1-source-files/images/P.DIALS.bin"
PRINT "PYTHON = ", ~P%
INCBIN "3-assembled-output/PYTHON.bin"
PRINT "P.ELITE = ", ~P%
INCBIN "1-source-files/images/P.ELITE.bin"
PRINT "P.A-SOFT = ", ~P%
INCBIN "1-source-files/images/P.A-SOFT.bin"
PRINT "P.(C)ASFT = ", ~P%
INCBIN "1-source-files/images/P.(C)ASFT.bin"
.run
JMP ENTRY \ Jump to ENTRY to start the loading process
\ ******************************************************************************
\
\ Name: B%
\ Type: Variable
\ Category: Drawing the screen
\ Summary: VDU commands for setting the square mode 4 screen
\ Deep dive: The split-screen mode in BBC Micro Elite
\ Drawing monochrome pixels on the BBC Micro
\
\ ------------------------------------------------------------------------------
\
\ This block contains the bytes that get written by OSWRCH to set up the screen
\ mode (this is equivalent to using the VDU statement in BASIC).
\
\ It defines the whole screen using a square, monochrome mode 4 configuration;
\ the mode 5 part for the dashboard is implemented in the IRQ1 routine.
\
\ The top part of Elite's screen mode is based on mode 4 but with the following
\ differences:
\
\ * 32 columns, 31 rows (256 x 248 pixels) rather than 40, 32
\
\ * The horizontal sync position is at character 45 rather than 49, which
\ pushes the screen to the right (which centres it as it's not as wide as
\ the normal screen modes)
\
\ * Screen memory goes from &6000 to &7EFF, which leaves another whole page
\ for code (i.e. 256 bytes) after the end of the screen. This is where the
\ Python ship blueprint slots in
\
\ * The text window is 1 row high and 13 columns wide, and is at (2, 16)
\
\ * The cursor is disabled
\
\ This almost-square mode 4 variant makes life a lot easier when drawing to the
\ screen, as there are 256 pixels on each row (or, to put it in screen memory
\ terms, there's one page of memory per row of pixels).
\
\ There is also an interrupt-driven routine that switches the bytes-per-pixel
\ setting from that of mode 4 to that of mode 5, when the raster reaches the
\ split between the space view and the dashboard.
\
\ ******************************************************************************
.B%
EQUB 22, 4 \ Switch to screen mode 4
EQUB 28 \ Define a text window as follows:
EQUB 2, 17, 15, 16 \
\ * Left = 2
\ * Right = 15
\ * Top = 16
\ * Bottom = 17
\
\ i.e. 1 row high, 13 columns wide at (2, 16)
EQUB 23, 0, 6, 31 \ Set 6845 register R6 = 31
EQUB 0, 0, 0 \
EQUB 0, 0, 0 \ This is the "vertical displayed" register, and sets
\ the number of displayed character rows to 31. For
\ comparison, this value is 32 for standard modes 4 and
\ 5, but we claw back the last row for storing code just
\ above the end of screen memory
EQUB 23, 0, 12, &0C \ Set 6845 register R12 = &0C and R13 = &00
EQUB 0, 0, 0 \
EQUB 0, 0, 0 \ This sets 6845 registers (R12 R13) = &0C00 to point
EQUB 23, 0, 13, &00 \ to the start of screen memory in terms of character
EQUB 0, 0, 0 \ rows. There are 8 pixel lines in each character row,
EQUB 0, 0, 0 \ so to get the actual address of the start of screen
\ memory, we multiply by 8:
\
\ &0C00 * 8 = &6000
\
\ So this sets the start of screen memory to &6000
EQUB 23, 0, 1, 32 \ Set 6845 register R1 = 32
EQUB 0, 0, 0 \
EQUB 0, 0, 0 \ This is the "horizontal displayed" register, which
\ defines the number of character blocks per horizontal
\ character row. For comparison, this value is 40 for
\ modes 4 and 5, but our custom screen is not as wide at
\ only 32 character blocks across
EQUB 23, 0, 2, 45 \ Set 6845 register R2 = 45
EQUB 0, 0, 0 \
EQUB 0, 0, 0 \ This is the "horizontal sync position" register, which
\ defines the position of the horizontal sync pulse on
\ the horizontal line in terms of character widths from
\ the left-hand side of the screen. For comparison this
\ is 49 for modes 4 and 5, but needs to be adjusted for
\ our custom screen's width
EQUB 23, 0, 10, 32 \ Set 6845 register R10 = %00100000 = 32
EQUB 0, 0, 0 \
EQUB 0, 0, 0 \ This is the "cursor start" register, and bits 5 and 6
\ define the "cursor display mode", as follows:
\
\ * %00 = steady, non-blinking cursor
\
\ * %01 = do not display a cursor
\
\ * %10 = fast blinking cursor (blink at 1/16 of the
\ field rate)
\
\ * %11 = slow blinking cursor (blink at 1/32 of the
\ field rate)
\
\ We can therefore turn off the cursor completely by
\ setting cursor display mode %01, with bit 6 of R10
\ clear and bit 5 of R10 set
\ ******************************************************************************
\
\ Name: E%
\ Type: Variable
\ Category: Sound
\ Summary: Sound envelope definitions
\
\ ------------------------------------------------------------------------------
\
\ This table contains the sound envelope data, which is passed to OSWORD by the
\ FNE macro to create the four sound envelopes used in-game. Refer to chapter 30
\ of the "BBC Microcomputer User Guide" by John Coll for details of sound
\ envelopes and what all the parameters mean.
\
\ The envelopes are as follows:
\
\ * Envelope 1 is the sound of our own laser firing
\
\ * Envelope 2 is the sound of lasers hitting us, or hyperspace
\
\ * Envelope 3 is the first sound in the two-part sound of us dying, or the
\ second sound in the two-part sound of us hitting or killing an enemy ship
\
\ * Envelope 4 is the sound of E.C.M. firing
\
\ ******************************************************************************
.E%
EQUB 1, 1, 0, 111, -8, 4, 1, 8, 8, -2, 0, -1, 112, 44
EQUB 2, 1, 14, -18, -1, 44, 32, 50, 6, 1, 0, -2, 120, 126
EQUB 3, 1, 1, -1, -3, 17, 32, 128, 1, 0, 0, -1, 1, 1
EQUB 4, 1, 4, -8, 44, 4, 6, 8, 22, 0, 0, -127, 126, 0
\ ******************************************************************************
\
\ Name: swine
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Resets the machine if the copy protection detects a problem
\
\ ******************************************************************************
.swine
LDA #%01111111 \ Set 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bits 0-6 (i.e. disable all hardware
\ interrupts from the System VIA)
JMP (&FFFC) \ Jump to the address in &FFFC to reset the machine
\ ******************************************************************************
\
\ Name: OSB
\ Type: Subroutine
\ Category: Utility routines
\ Summary: A convenience routine for calling OSBYTE with Y = 0
\
\ ******************************************************************************
.OSB
LDY #0 \ Call OSBYTE with Y = 0, returning from the subroutine
JMP OSBYTE \ using a tail call (so we can call OSB to call OSBYTE
\ for when we know we want Y set to 0)
\ ******************************************************************************
\
\ Name: Authors' names
\ Type: Variable
\ Category: Copy protection
\ Summary: The authors' names, buried in the code
\
\ ------------------------------------------------------------------------------
\
\ Contains the authors' names, plus an unused OS command string that would
\ *RUN the main game code, which isn't what actually happens (so presumably
\ this is to throw the crackers off the scent).
\
\ ******************************************************************************
EQUS "R.ELITEcode" \ This is short for "*RUN ELITEcode"
EQUB 13
EQUS "By D.Braben/I.Bell"
EQUB 13
EQUB &B0
\ ******************************************************************************
\
\ Name: oscliv
\ Type: Variable
\ Category: Utility routines
\ Summary: Contains the address of OSCLIV, for executing OS commands
\
\ ******************************************************************************
.oscliv
EQUW &FFF7 \ Address of OSCLIV, for executing OS commands
\ (specifically the *LOAD that loads the main game code)
\ ******************************************************************************
\
\ Name: David9
\ Type: Variable
\ Category: Copy protection
\ Summary: Address used as part of the stack-based decryption loop
\
\ ------------------------------------------------------------------------------
\
\ This address is used in the decryption loop starting at David2 in part 4, and
\ is used to jump back into the loop at David5.
\
\ ******************************************************************************
.David9
EQUW David5 \ The address of David5
CLD \ This instruction is not used
\ ******************************************************************************
\
\ Name: David23
\ Type: Variable
\ Category: Copy protection
\ Summary: Address pointer to the start of the 6502 stack
\
\ ------------------------------------------------------------------------------
\
\ This two-byte address points to the start of the 6502 stack, which descends
\ from the end of page 2, less LEN bytes, which comes out as &01DF. So when we
\ push 33 bytes onto the stack (LEN being 33), this address will point to the
\ start of those bytes, which means we can push executable code onto the stack
\ and run it by calling this address with a JMP (David23) instruction. Sneaky
\ stuff!
\
\ ******************************************************************************
.David23
EQUW (512-LEN) \ The address of LEN bytes before the start of the stack
\ ******************************************************************************
\
\ Name: doPROT1
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Routine to self-modify the loader code
\
\ ------------------------------------------------------------------------------
\
\ This routine modifies various bits of code in-place as part of the copy
\ protection mechanism. It is called with A = &48 and X = 255.
\
\ ******************************************************************************
.doPROT1
LDY #&DB \ Store &EFDB in TRTB%(1 0) to point to the keyboard
STY TRTB% \ translation table for OS 0.1 (which we will overwrite
LDY #&EF \ with a call to OSBYTE later)
STY TRTB%+1
LDY #2 \ Set the high byte of V219(1 0) to 2
STY V219+1
STA PROT1-255,X \ Poke &48 into PROT1, which changes the instruction
\ there to a PHA
LDY #&18 \ Set the low byte of V219(1 0) to &18 (as X = 255), so
STY V219+1,X \ V219(1 0) now contains &0218
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: MHCA
\ Type: Variable
\ Category: Copy protection
\ Summary: Used to set one of the vectors in the copy protection code
\
\ ------------------------------------------------------------------------------
\
\ This value is used to set the low byte of BLPTR(1 0), when it's set in PLL1
\ as part of the copy protection.
\
\ ******************************************************************************
.MHCA
EQUB &CA \ The low byte of BLPTR(1 0)
\ ******************************************************************************
\
\ Name: David7
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Part of the multi-jump obfuscation code in PROT1
\
\ ------------------------------------------------------------------------------
\
\ This instruction is part of the multi-jump obfuscation in PROT1 (see part 2 of
\ the loader), which does the following jumps:
\
\ David8 -> FRED1 -> David7 -> Ian1 -> David3
\
\ ******************************************************************************
.David7
BCC Ian1 \ This instruction is part of the multi-jump obfuscation
\ in PROT1
\ ******************************************************************************
\
\ Name: FNE
\ Type: Macro
\ Category: Sound
\ Summary: Macro definition for defining a sound envelope
\
\ ------------------------------------------------------------------------------
\
\ The following macro is used to define the four sound envelopes used in the
\ game. It uses OSWORD 8 to create an envelope using the 14 parameters in the
\ I%-th block of 14 bytes at location E%. This OSWORD call is the same as BBC
\ BASIC's ENVELOPE command.
\
\ See variable E% for more details of the envelopes themselves.
\
\ ******************************************************************************
MACRO FNE I%
LDX #LO(E%+I%*14) \ Set (Y X) to point to the I%-th set of envelope data
LDY #HI(E%+I%*14) \ in E%
LDA #8 \ Call OSWORD with A = 8 to set up sound envelope I%
JSR OSWORD
ENDMACRO
\ ******************************************************************************
\
\ Name: Elite loader (Part 2 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Perform a number of OS calls, set up sound, push routines on stack
\
\ ------------------------------------------------------------------------------
\
\ This part of the loader does a number of calls to OS routines, sets up the
\ sound envelopes, pushes 33 bytes onto the stack that will be used later, and
\ sends us on a wild goose chase, just for kicks.
\
\ ------------------------------------------------------------------------------
\
\ Other entry points:
\
\ Ian1 Re-entry point following the wild goose chase
\ obfuscation
\
\ ******************************************************************************
.ENTRY
SEI \ Disable all interrupts
CLD \ Clear the decimal flag, so we're not in decimal mode
IF NOT(DISC)
LDA #0 \ Call OSBYTE with A = 0 and X = 255 to fetch the
LDX #255 \ operating system version into X
JSR OSBYTE
TXA \ If X = 0 then this is OS 1.00, so jump down to OS100
BEQ OS100 \ to skip the following
LDY &FFB6 \ Otherwise this is OS 1.20, so set Y to the contents of
\ &FFB6, which contains the length of the default vector
\ table
LDA &FFB7 \ Set ZP(1 0) to the location stored in &FFB7-&FFB8,
STA ZP \ which contains the address of the default vector table
LDA &FFB8
STA ZP+1
DEY \ Decrement Y so we can use it as an index for setting
\ all the vectors to their default states
.ABCDEFG
LDA (ZP),Y \ Copy the Y-th byte from the default vector table into
STA &0200,Y \ the vector table in &0200
DEY \ Decrement the loop counter
BPL ABCDEFG \ Loop back for the next vector until we have done them
\ all
.OS100
ENDIF
LDA #%01111111 \ Set 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bits 0-6 (i.e. disable all hardware
\ interrupts from the System VIA)
STA &FE6E \ Set 6522 User VIA interrupt enable register IER
\ (SHEILA &6E) bits 0-6 (i.e. disable all hardware
\ interrupts from the User VIA)
LDA &FFFC \ Fetch the low byte of the reset address in &FFFC,
\ which will reset the machine if called
STA &0200 \ Set the low bytes of USERV, BRKV, IRQ2V and EVENTV
STA &0202
STA &0206
STA &0220
LDA &FFFD \ Fetch the high byte of the reset address in &FFFD,
\ which will reset the machine if called
STA &0201 \ Set the high bytes of USERV, BRKV, IRQ2V and EVENTV
STA &0203
STA &0207
STA &0221
LDX #&2F-2 \ We now step through all the vectors from &0204 to
\ &022F and OR their high bytes with &C0, so they all
\ point into the MOS ROM space (which is from &C000 and
\ upwards), so we set a counter in X to count through
\ them
.purge
LDA &0202,X \ Set the high byte of the vector in &0202+X so it
ORA #&C0 \ points to the MOS ROM
STA &0202,X
DEX \ Increment the counter to point to the next high byte
DEX
BPL purge \ Loop back until we have done all the vectors
LDA #&60 \ Store an RTS instruction in location &0232
STA &0232
LDA #&2 \ Point the NETV vector to &0232, which we just filled
STA NETV+1 \ with an RTS
LDA #&32
STA NETV
LDA #&20 \ Set A to the op code for a JSR call with absolute
\ addressing
EQUB &2C \ Skip the next instruction by turning it into
\ &2C &D0 &66, or BIT &66D0, which does nothing apart
\ from affect the flags
.Ian1
BNE David3 \ This instruction is skipped if we came from above,
\ otherwise this is part of the multi-jump obfuscation
\ in PROT1
STA David2 \ Store &20 in location David2, which modifies the
\ instruction there (see David2 for details)
LSR A \ Set A = 16
LDX #3 \ Set the high bytes of BLPTR(1 0), BLN(1 0) and
STX BLPTR+1 \ EXCN(1 0) to &3. We will fill in the high bytes in
STX BLN+1 \ the PLL1 routine, and will then use these values in
STX EXCN+1 \ the IRQ1 handler
DEX \ Set X = 2
JSR OSBYTE \ Call OSBYTE with A = 16 and X = 2 to set the ADC to
\ sample 2 channels from the joystick
EQUB &2C \ Skip the next instruction by turning it into
\ &2C &D0 &A1, or BIT &A1D0, which does nothing apart
\ from affect the flags
.FRED1
BNE David7 \ This instruction is skipped if we came from above,
\ otherwise this is part of the multi-jump obfuscation
\ in PROT1
LDX #255 \ Call doPROT1 to change an instruction in the PROT1
LDA #&48 \ routine and set up another couple of variables
JSR doPROT1
LDA #144 \ Call OSBYTE with A = 144, X = 255 and Y = 0 to move
JSR OSB \ the screen down one line and turn screen interlace on
LDA #247 \ Call OSBYTE with A = 247 and X = Y = 0 to disable the
LDX #0 \ BREAK intercept code by poking 0 into the first value
JSR OSB
\LDA #129 \ These instructions are commented out in the original
\LDY #255 \ source, along with the comment "Damn 0.1", so
\LDX #1 \ presumably MOS version 0.1 was a bit of a pain to
\JSR OSBYTE \ support - which is probably why Elite doesn't bother
\ \ and only supports 1.0 and 1.2
\TXA
\
\BPL OS01
\
\Damn 0.1
LDA #190 \ Call OSBYTE with A = 190, X = 8 and Y = 0 to set the
LDX #8 \ ADC conversion type to 8 bits, for the joystick
JSR OSB
EQUB &2C \ Skip the next instruction by turning it into
\ &2C &D0 &E1, or BIT &E1D0, which does nothing apart
\ from affect the flags
.David8
BNE FRED1 \ This instruction is skipped if we came from above,
\ otherwise this is part of the multi-jump obfuscation
\ in PROT1
LDA #143 \ Call OSBYTE 143 to issue a paged ROM service call of
LDX #&C \ type &C with argument &FF, which is the "NMI claim"
LDY #&FF \ service call that asks the current user of the NMI
JSR OSBYTE \ space to clear it out
LDA #13 \ Set A = 13 for the next OSBYTE call
.abrk
LDX #0 \ Call OSBYTE with A = 13, X = 0 and Y = 0 to disable
JSR OSB \ the "output buffer empty" event
LDA #225 \ Call OSBYTE with A = 225, X = 128 and Y = 0 to set
LDX #128 \ the function keys to return ASCII codes for SHIFT-fn
JSR OSB \ keys (i.e. add 128)
LDA #172 \ Call OSBYTE 172 to read the address of the MOS
LDX #0 \ keyboard translation table into (Y X)
LDY #255
JSR OSBYTE
STX TRTB% \ Store the address of the keyboard translation table in
STY TRTB%+1 \ TRTB%(1 0)
LDA #200 \ Call OSBYTE with A = 200, X = 3 and Y = 0 to disable
LDX #3 \ the ESCAPE key and clear memory if the BREAK key is
JSR OSB \ pressed
IF PROT AND NOT(DISC)
CPX #3 \ If the previous value of X from the call to OSBYTE 200
BNE abrk+1 \ was not 3 (ESCAPE disabled, clear memory), jump to
\ abrk+1, which contains a BRK instruction which will
\ reset the computer (as we set BRKV to point to the
\ reset address above)
ENDIF
LDA #13 \ Call OSBYTE with A = 13, X = 2 and Y = 0 to disable
LDX #2 \ the "character entering keyboard buffer" event
JSR OSB
.OS01
LDX #&FF \ Set the stack pointer to &01FF, which is the standard
TXS \ location for the 6502 stack, so this instruction
\ effectively resets the stack
INX \ Set X = 0, to use as a counter in the following loop
.David3
LDA BEGIN%,X \ This routine pushes 33 bytes from BEGIN% onto the
\ stack, so fetch the X-th byte from BEGIN%
.PROT1
INY \ This instruction gets changed to a PHA instruction by
\ the doPROT1 routine that's called above, so by the
\ time we get here, this instruction actually pushes the
\ X-th byte from BEGIN% onto the stack
INX \ Increment the loop counter
CPX #LEN \ If X < #LEN (which is 33), loop back for the next one.
BNE David8 \ This branch actually takes us on a wild goose chase
\ through the following locations, where each BNE is
\ prefaced by an EQUB &2C that disables the branch
\ instruction during the normal instruction flow:
\
\ David8 -> FRED1 -> David7 -> Ian1 -> David3
\
\ so in the end this just loops back to push the next
\ byte onto the stack, but in a really sneaky way
LDA #LO(B%) \ Set the low byte of ZP(1 0) to point to the VDU code
STA ZP \ table at B%
LDA #&C8 \ Poke &C8 into PROT1 to change the instruction that we
STA PROT1 \ modified back to an INY instruction, rather than a PHA
LDA #HI(B%) \ Set the high byte of ZP(1 0) to point to the VDU code
STA ZP+1 \ table at B%
LDY #0 \ We are now going to send the N% VDU bytes in the table
\ at B% to OSWRCH to set up the special mode 4 screen
\ that forms the basis for the split-screen mode
.LOOP
LDA (ZP),Y \ Pass the Y-th byte of the B% table to OSWRCH
JSR OSWRCH
INY \ Increment the loop counter
CPY #N% \ Loop back for the next byte until we have done them
BNE LOOP \ all (the number of bytes was set in N% above)
LDA #1 \ In doPROT1 above we set V219(1 0) = &0218, so this
TAX \ code sets the contents of &0219 (the high byte of
TAY \ BPUTV) to 1. We will see why this later, at the start
STA (V219),Y \ of part 4
LDA #4 \ Call OSBYTE with A = 4, X = 1 and Y = 0 to disable
JSR OSB \ cursor editing, so the cursor keys return ASCII values
\ and can therefore be used in-game
LDA #9 \ Call OSBYTE with A = 9, X = 0 and Y = 0 to disable
LDX #0 \ flashing colours
JSR OSB
LDA #&6C \ Poke &6C into crunchit after EOR'ing it first (which
EOR crunchit \ has no effect as crunchit contains a BRK instruction
STA crunchit \ with opcode 0), to change crunchit to an indirect JMP
FNE 0 \ Set up sound envelopes 0-3 using the FNE macro
FNE 1
FNE 2
FNE 3
\ ******************************************************************************
\
\ Name: Elite loader (Part 3 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Move and decrypt recursive tokens, Python blueprint and images
\
\ ------------------------------------------------------------------------------
\
\ Move and decrypt the following memory blocks:
\
\ * WORDS9: move 4 pages (1024 bytes) from CODE% to &0400
\
\ * P.ELITE: move 1 page (256 bytes) from CODE% + &0C00 to &6300
\
\ * P.A-SOFT: move 1 page (256 bytes) from CODE% + &0D00 to &6100
\
\ * P.(C)ASFT: move 1 page (256 bytes) from CODE% + &0E00 to &7600
\
\ * P.DIALS and PYTHON: move 8 pages (2048 bytes) from CODE% + &0400 to &7800
\
\ * Move 2 pages (512 bytes) from UU% to &0B00-&0CFF
\
\ and call the routine to draw Saturn between P.(C)ASFT and P.DIALS.
\
\ See part 1 above for more details on the above files and the locations that
\ they are moved to.
\
\ The code at UU% (see below) forms part of the loader code and is moved before
\ being run, so it's tucked away safely while the main game code is loaded and
\ decrypted.
\
\ ******************************************************************************
LDX #4 \ Set the following:
STX P+1 \
LDA #HI(CODE%) \ P(1 0) = &0400
STA ZP+1 \ ZP(1 0) = CODE%
LDY #0 \ (X Y) = &400 = 1024
LDA #256-LEN1 \
STA (V219-4,X) \ In doPROT1 above we set V219(1 0) = &0218, so this
STY ZP \ also sets the contents of &0218 (the low byte of
STY P \ BPUTV) to 256 - LEN1, or &F1. We set the low byte to
\ 1 above, so BPUTV now contains &01F1, which we will
\ use at the start of part 4
JSR crunchit \ Call crunchit, which has now been modified to call the
\ MVDL routine on the stack, to move and decrypt &400
\ bytes from CODE% to &0400. We loaded WORDS9.bin to
\ CODE% in part 1, so this moves WORDS9
LDX #1 \ Set the following:
LDA #(HI(CODE%)+&C) \
STA ZP+1 \ P(1 0) = &6300
LDA #&63 \ ZP(1 0) = CODE% + &C
STA P+1 \ (X Y) = &100 = 256
LDY #0
JSR crunchit \ Call crunchit to move and decrypt &100 bytes from
\ CODE% + &C to &6300, so this moves P.ELITE
LDX #1 \ Set the following:
LDA #(HI(CODE%)+&D) \
STA ZP+1 \ P(1 0) = &6100
LDA #&61 \ ZP(1 0) = CODE% + &D
STA P+1 \ (X Y) = &100 = 256
LDY #0
JSR crunchit \ Call crunchit to move and decrypt &100 bytes from
\ CODE% + &D to &6100, so this moves P.A-SOFT
LDX #1 \ Set the following:
LDA #(HI(CODE%)+&E) \
STA ZP+1 \ P(1 0) = &7600
LDA #&76 \ ZP(1 0) = CODE% + &E
STA P+1 \ (X Y) = &100 = 256
LDY #0
JSR crunchit \ Call crunchit to move and decrypt &100 bytes from
\ CODE% + &E to &7600, so this moves P.(C)ASFT
JSR PLL1 \ Call PLL1 to draw Saturn
LDX #8 \ Set the following:
LDA #(HI(CODE%)+4) \
STA ZP+1 \ P(1 0) = &7800
LDA #&78 \ ZP(1 0) = CODE% + &4
STA P+1 \ (X Y) = &800 = 2048
LDY #0 \
STY ZP \ Also set BLCNT = 0
STY BLCNT
STY P
JSR crunchit \ Call crunchit to move and decrypt &800 bytes from
\ CODE% + &4 to &7800, so this moves P.DIALS and PYTHON
IF DISC
LDX #2 \ Set the following:
LDA #HI(UU%) \
STA ZP+1 \ P(1 0) = LE%
LDA #LO(UU%) \ ZP(1 0) = UU%
STA ZP \ (X Y) = &200 = 512 (as we are building for disc)
LDA #HI(LE%)
STA P+1
LDY #0
STY P
ELSE
LDX #3 \ Set the following:
LDA #HI(UU%) \
STA ZP+1 \ P(1 0) = LE%
LDA #LO(UU%) \ ZP(1 0) = UU%
STA ZP \ (X Y) = &300 = 768 (as we are building for tape)
LDA #HI(LE%)
STA P+1
LDY #0
STY P
ENDIF
JSR crunchit \ Call crunchit to move and decrypt either &200 or &300
\ bytes from UU% to LE%, leaving X = 0
\ ******************************************************************************
\
\ Name: Elite loader (Part 4 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Copy more code onto stack, decrypt TUT block, set up IRQ1 handler
\
\ ------------------------------------------------------------------------------
\
\ This part copies more code onto the stack (from BLOCK to ENDBLOCK), decrypts
\ the code from TUT onwards, and sets up the IRQ1 handler for the split-screen
\ mode.
\
\ ******************************************************************************
STY David3-2 \ Y was set to 0 above, so this modifies the OS01
\ routine above by changing the TXS instruction to BRK,
\ so calls to OS01 will now do this:
\
\ LDX #&FF
\ BRK
\
\ This is presumably just to confuse any cracker, as we
\ don't call OS01 again
\ We now enter a loop that starts with the counter in Y
\ (initially set to 0). It calls JSR &01F1 on the stack,
\ which pushes the Y-th byte of BLOCK on the stack
\ before encrypting the Y-th byte of BLOCK in-place. It
\ then jumps back to David5 below, where we increment Y
\ until it reaches a value of ENDBLOCK - BLOCK. So this
\ loop basically decrypts the code from TUT onwards, and
\ at the same time it pushes the code between BLOCK and
\ ENDBLOCK onto the stack, so it's there ready to be run
\ (at address &0163)
.David2
EQUB &AC \ This byte was changed to &20 by part 2, so by the time
EQUW &FFD4 \ we get here, these three bytes together become JSR
\ &FFD4, or JSR OSBPUT. Amongst all the code above,
\ we've also managed to set BPUTV to &01F1, and as BPUTV
\ is the vector that OSBPUT goes through, these three
\ bytes are actually doing JSR &01F1
\
\ That address is in the stack, and is the address of
\ the BEGIN% routine, which we pushed onto the stack in
\ the modified PROT1 routine. That routine doesn't
\ return with an RTS, but instead it removes the return
\ address from the stack and jumps to David5 below after
\ pushing the Y-th byte of BLOCK onto the stack and
\ EOR'ing the Y-th byte of TUT with the Y-th byte of
\ BLOCK
\
\ This obfuscation probably kept the crackers busy for a
\ while - it's difficult enough to work out when you
\ have the source code in front of you!
.LBLa
\ If, for some reason, the above JSR doesn't call the
\ routine on the stack and returns normally, which might
\ happen if crackers manage to unpick the BPUTV
\ redirection, then we end up here. We now obfuscate the
\ first 255 bytes of the location where the main game
\ gets loaded (which is set in C%), just to make things
\ hard, and then we reset the machine... all in a
\ completely twisted manner, of course
LDA C%,X \ Obfuscate the X-th byte of C% by EOR'ing with &A5
EOR #&A5
STA C%,X
DEX \ Decrement the loop counter
BNE LBLa \ Loop back until X wraps around, after EOR'ing a whole
\ page
JMP (C%+&CF) \ C%+&CF is &100F, which in the main game code contains
\ an LDA KY17 instruction (it's in the main loader in
\ the MA76 section). This has opcode &A5 &4E, and the
\ EOR above changes the first of these to &00, so this
\ jump goes to a BRK instruction, which in turn goes to
\ BRKV, which in turn resets the computer (as we set
\ BRKV to point to the reset address in part 2)
.swine2
JMP swine \ Jump to swine to reset the machine
EQUW &4CFF \ This data doesn't appear to be used
.crunchit
BRK \ This instruction gets changed to an indirect JMP at
EQUW David23 \ the end of part 2, so this does JMP (David23). David23
\ contains &01DF, so these bytes are actually doing JMP
\ &01DF. That address is in the stack, and is the
\ address of the MVDL routine, which we pushed onto the
\ stack in the modified PROT1 routine... so this
\ actually does the following:
\
\ JMP MVDL
\
\ meaning that this instruction:
\
\ JSR crunchit
\
\ actually does this, because it's a tail call:
\
\ JSR MVDL
\
\ It's yet another impressive bit of obfuscation and
\ misdirection
.RAND
EQUD &6C785349 \ The random number seed used for drawing Saturn
.David5
INY \ Increment the loop counter
CPY #(ENDBLOCK-BLOCK) \ Loop back to copy the next byte until we have copied
BNE David2 \ all the bytes between BLOCK and ENDBLOCK
SEI \ Disable interrupts while we set up our interrupt
\ handler to support the split-screen mode
LDA #%11000010 \ Clear 6522 System VIA interrupt enable register IER
STA VIA+&4E \ (SHEILA &4E) bits 1 and 7 (i.e. enable CA1 and TIMER1
\ interrupts from the System VIA, which enable vertical
\ sync and the 1 MHz timer, which we need enabled for
\ the split-screen interrupt code to work)
LDA #%01111111 \ Set 6522 User VIA interrupt enable register IER
STA &FE6E \ (SHEILA &6E) bits 0-7 (i.e. disable all hardware
\ interrupts from the User VIA)
LDA IRQ1V \ Store the low byte of the current IRQ1V vector in VEC
STA VEC
LDA IRQ1V+1 \ If the current high byte of the IRQ1V vector is less
BPL swine2 \ than &80, which means it points to user RAM rather
\ the MOS ROM, then something is probably afoot, so jump
\ to swine2 to reset the machine
STA VEC+1 \ Otherwise all is well, so store the high byte of the
\ current IRQ1V vector in VEC+1, so VEC(1 0) now
\ contains the original address of the IRQ1 handler
LDA #HI(IRQ1) \ Set the IRQ1V vector to IRQ1, so IRQ1 is now the
STA IRQ1V+1 \ interrupt handler
LDA #LO(IRQ1)
STA IRQ1V
LDA #VSCAN \ Set 6522 System VIA T1C-L timer 1 high-order counter
STA VIA+&45 \ (SHEILA &45) to VSCAN (56) to start the T1 counter
\ counting down from 14080 at a rate of 1 MHz (this is
\ a different value to the main game code)
CLI \ Re-enable interrupts
IF DISC
LDA #%10000001 \ Clear 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bit 1 (i.e. enable the CA2 interrupt,
\ which comes from the keyboard)
LDY #20 \ Set Y = 20 for the following OSBYTE call
IF _REMOVE_CHECKSUMS
NOP \ If we have disabled checksums, skip the OSBYTE call
NOP
NOP
ELSE
JSR OSBYTE \ A was set to 129 above, so this calls OSBYTE with
\ A = 129 and Y = 20, as well as a value of X = 0,
\ which was set by the call to crunchit at the end of
\ part 3
\
\ This reads the keyboard with a time limit of (Y X)
\ centiseconds, or 20 * 256 = 5120 centiseconds, or
\ 51.2 seconds
ENDIF
LDA #%00000001 \ Set 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bit 1 (i.e. disable the CA2 interrupt,
\ which comes from the keyboard)
ENDIF
RTS \ This RTS actually does a jump to ENTRY2, to the next
\ step of the loader in part 5. See the documentation
\ for the stack routine at BEGIN% for more details
\ ******************************************************************************
\
\ Name: PLL1 (Part 1 of 3)
\ Type: Subroutine
\ Category: Drawing planets
\ Summary: Draw Saturn on the loading screen (draw the planet)
\ Deep dive: Drawing Saturn on the loading screen
\
\ ******************************************************************************
.PLL1
\ The following loop iterates CNT(1 0) times, i.e. &500
\ or 1280 times, and draws the planet part of the
\ loading screen's Saturn
LDA VIA+&44 \ Read the 6522 System VIA T1C-L timer 1 low-order
STA RAND+1 \ counter (SHEILA &44), which decrements one million
\ times a second and will therefore be pretty random,
\ and store it in location RAND+1, which is among the
\ main game code's random seeds in RAND (so this seeds
\ the random number generator)
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r1
JSR SQUA2 \ Set (A P) = A * A
\ = r1^2
STA ZP+1 \ Set ZP(1 0) = (A P)
LDA P \ = r1^2
STA ZP
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r2
STA YY \ Set YY = A
\ = r2
JSR SQUA2 \ Set (A P) = A * A
\ = r2^2
TAX \ Set (X P) = (A P)
\ = r2^2
LDA P \ Set (A ZP) = (X P) + ZP(1 0)
ADC ZP \
STA ZP \ first adding the low bytes
TXA \ And then adding the high bytes
ADC ZP+1
BCS PLC1 \ If the addition overflowed, jump down to PLC1 to skip
\ to the next pixel
STA ZP+1 \ Set ZP(1 0) = (A ZP)
\ = r1^2 + r2^2
LDA #1 \ Set ZP(1 0) = &4001 - ZP(1 0) - (1 - C)
SBC ZP \ = 128^2 - ZP(1 0)
STA ZP \
\ (as the C flag is clear), first subtracting the low
\ bytes
LDA #&40 \ And then subtracting the high bytes
SBC ZP+1
STA ZP+1
BCC PLC1 \ If the subtraction underflowed, jump down to PLC1 to
\ skip to the next pixel
\ If we get here, then both calculations fitted into
\ 16 bits, and we have:
\
\ ZP(1 0) = 128^2 - (r1^2 + r2^2)
\
\ where ZP(1 0) >= 0
JSR ROOT \ Set ZP = SQRT(ZP(1 0))
LDA ZP \ Set X = ZP >> 1
LSR A \ = SQRT(128^2 - (a^2 + b^2)) / 2
TAX
LDA YY \ Set A = YY
\ = r2
CMP #128 \ If YY >= 128, set the C flag (so the C flag is now set
\ to bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so A is
\ halved while retaining its sign
\
\ A is still a signed number from -128 to 127
JSR PIX \ Draw a pixel at screen coordinate (X + 128, A + 128),
\ where:
\
\ X = ZP / 2
\ A = r2 / 2
\ ZP = SQRT(128^2 - (r1^2 + r2^2))
\
\ So this is the same as plotting at (x, y) where:
\
\ r1 = random number from -128 to 127
\ r2 = random number from -128 to 127
\
\ (r1^2 + r2^2) < 128^2
\
\ x = (SQRT(128^2 - (r1^2 + r2^2)) / 2) + 128
\ y = (r2 / 2) + 128
\
\ which is what we want
.PLC1
DEC CNT \ Decrement the counter in CNT (the low byte)
BNE PLL1 \ Loop back to PLL1 until CNT = 0
DEC CNT+1 \ Decrement the counter in CNT+1 (the high byte)
BNE PLL1 \ Loop back to PLL1 until CNT+1 = 0
LDX #&C2 \ Set the low byte of EXCN(1 0) to &C2, so we now have
STX EXCN \ EXCN(1 0) = &03C2, which we will use in the IRQ1
\ handler (this has nothing to do with drawing Saturn,
\ it's all part of the copy protection)
\ ******************************************************************************
\
\ Name: PLL1 (Part 2 of 3)
\ Type: Subroutine
\ Category: Drawing planets
\ Summary: Draw Saturn on the loading screen (draw the stars)
\ Deep dive: Drawing Saturn on the loading screen
\
\ ******************************************************************************
\ The following loop iterates CNT2(1 0) times, i.e. &1DD
\ or 477 times, and draws the background stars on the
\ loading screen
.PLL2
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r3
TAX \ Set X = A
\ = r3
JSR SQUA2 \ Set (A P) = A * A
\ = r3^2
STA ZP+1 \ Set ZP+1 = A
\ = r3^2 / 256
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r4
STA YY \ Set YY = r4
JSR SQUA2 \ Set (A P) = A * A
\ = r4^2
ADC ZP+1 \ Set A = A + r3^2 / 256
\ = r4^2 / 256 + r3^2 / 256
\ = (r3^2 + r4^2) / 256
CMP #&11 \ If A < 17, jump down to PLC2 to skip to the next pixel
BCC PLC2
LDA YY \ Set A = r4
JSR PIX \ Draw a pixel at screen coordinate (X + 128, A + 128),
\ where:
\
\ X = r3
\ A = r4
\
\ So this is the same as plotting at (x, y) where:
\
\ r3 = random number from -128 to 127
\ r4 = random number from -128 to 127
\
\ (r3^2 + r4^2) / 256 >= 17
\
\ x = r3
\ y = r4
\
\ which is what we want
.PLC2
DEC CNT2 \ Decrement the counter in CNT2 (the low byte)
BNE PLL2 \ Loop back to PLL2 until CNT2 = 0
DEC CNT2+1 \ Decrement the counter in CNT2+1 (the high byte)
BNE PLL2 \ Loop back to PLL2 until CNT2+1 = 0
LDX MHCA \ Set the low byte of BLPTR(1 0) to the contents of MHCA
STX BLPTR \ (which is &CA), so we now have BLPTR(1 0) = &03CA,
\ which we will use in the IRQ1 handler (this has
\ nothing to do with drawing Saturn, it's all part of
\ the copy protection)
LDX #&C6 \ Set the low byte of BLN(1 0) to &C6, so we now have
STX BLN \ BLN(1 0) = &03C6, which we will use in the IRQ1
\ handler (this has nothing to do with drawing Saturn,
\ it's all part of the copy protection)
\ ******************************************************************************
\
\ Name: PLL1 (Part 3 of 3)
\ Type: Subroutine
\ Category: Drawing planets
\ Summary: Draw Saturn on the loading screen (draw the rings)
\ Deep dive: Drawing Saturn on the loading screen
\
\ ******************************************************************************
\ The following loop iterates CNT3(1 0) times, i.e. &500
\ or 1280 times, and draws the rings around the loading
\ screen's Saturn
.PLL3
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r5
STA ZP \ Set ZP = r5
JSR SQUA2 \ Set (A P) = A * A
\ = r5^2
STA ZP+1 \ Set ZP+1 = A
\ = r5^2 / 256
JSR DORND \ Set A and X to signed random numbers between -128 and
\ 127, so let's say A = r6
STA YY \ Set YY = r6
JSR SQUA2 \ Set (A P) = A * A
\ = r6^2
STA T \ Set T = A
\ = r6^2 / 256
ADC ZP+1 \ Set ZP+1 = A + r5^2 / 256
STA ZP+1 \ = r6^2 / 256 + r5^2 / 256
\ = (r5^2 + r6^2) / 256
LDA ZP \ Set A = ZP
\ = r5
CMP #128 \ If A >= 128, set the C flag (so the C flag is now set
\ to bit 7 of ZP, i.e. bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so bits
\ 6 and 7 are now the same
CMP #128 \ If A >= 128, set the C flag (so again, the C flag is
\ set to bit 7 of A)
ROR A \ Rotate A and set the sign bit to the C flag, so bits
\ 5-7 are now the same, i.e. A is a random number in one
\ of these ranges:
\
\ %00000000 - %00011111 = 0-31
\ %11100000 - %11111111 = 224-255
\
\ In terms of signed 8-bit integers, this is a random
\ number from -32 to 31. Let's call it r7
ADC YY \ Set A = A + YY
\ = r7 + r6
TAX \ Set X = A
\ = r6 + r7
JSR SQUA2 \ Set (A P) = A * A
\ = (r6 + r7)^2
TAY \ Set Y = A
\ = (r6 + r7)^2 / 256
ADC ZP+1 \ Set A = A + ZP+1
\ = (r6 + r7)^2 / 256 + (r5^2 + r6^2) / 256
\ = ((r6 + r7)^2 + r5^2 + r6^2) / 256
BCS PLC3 \ If the addition overflowed, jump down to PLC3 to skip
\ to the next pixel
CMP #80 \ If A >= 80, jump down to PLC3 to skip to the next
BCS PLC3 \ pixel
CMP #32 \ If A < 32, jump down to PLC3 to skip to the next pixel
BCC PLC3
TYA \ Set A = Y + T
ADC T \ = (r6 + r7)^2 / 256 + r6^2 / 256
\ = ((r6 + r7)^2 + r6^2) / 256
CMP #16 \ If A >= 16, skip to PL1 to plot the pixel
BCS PL1
LDA ZP \ If ZP is positive (i.e. r5 < 128), jump down to PLC3
BPL PLC3 \ to skip to the next pixel
.PL1
\ If we get here then the following is true:
\
\ 32 <= ((r6 + r7)^2 + r5^2 + r6^2) / 256 < 80
\
\ and either this is true:
\
\ ((r6 + r7)^2 + r6^2) / 256 >= 16
\
\ or both these are true:
\
\ ((r6 + r7)^2 + r6^2) / 256 < 16
\ r5 >= 128
LDA YY \ Set A = YY
\ = r6
JSR PIX \ Draw a pixel at screen coordinate (X + 128, A + 128),
\ where:
\
\ X = (random -32 to 31) + r6
\ A = r6
\
\ So this is the same as plotting at (x, y) where:
\
\ r5 = random number from -128 to 127
\ r6 = random number from -128 to 127
\ r7 = r5, squashed into -32 to 31
\
\ 32 <= ((r6 + r7)^2 + r5^2 + r6^2) / 256 < 80
\
\ Either: ((r6 + r7)^2 + r6^2) / 256 >= 16
\
\ Or: ((r6 + r7)^2 + r6^2) / 256 < 16
\ r5 >= 128
\
\ x = r6 + r7 + 128
\ y = r6 + 128
\
\ which is what we want
.PLC3
DEC CNT3 \ Decrement the counter in CNT3 (the low byte)
BNE PLL3 \ Loop back to PLL3 until CNT3 = 0
DEC CNT3+1 \ Decrement the counter in CNT3+1 (the high byte)
BNE PLL3 \ Loop back to PLL3 until CNT3+1 = 0
\ ******************************************************************************
\
\ Name: DORND
\ Type: Subroutine
\ Category: Maths (Arithmetic)
\ Summary: Generate random numbers
\ Deep dive: Generating random numbers
\ Fixing ship positions
\
\ ------------------------------------------------------------------------------
\
\ Set A and X to random numbers (though note that X is set to the random number
\ that was returned in A the last time DORND was called).
\
\ The C and V flags are also set randomly.
\
\ This is a simplified version of the DORND routine in the main game code. It
\ swaps the two calculations around and omits the ROL A instruction, but is
\ otherwise very similar. See the DORND routine in the main game code for more
\ details.
\
\ ******************************************************************************
.DORND
LDA RAND+1 \ r1´ = r1 + r3 + C
TAX \ r3´ = r1
ADC RAND+3
STA RAND+1
STX RAND+3
LDA RAND \ X = r2´ = r0
TAX \ A = r0´ = r0 + r2
ADC RAND+2
STA RAND
STX RAND+2
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: SQUA2
\ Type: Subroutine
\ Category: Maths (Arithmetic)
\ Summary: Calculate (A P) = A * A
\ Deep dive: Shift-and-add multiplication
\
\ ------------------------------------------------------------------------------
\
\ Do the following multiplication of signed 8-bit numbers:
\
\ (A P) = A * A
\
\ This uses a similar approach to routine SQUA2 in the main game code, which
\ itself uses the MU11 routine to do the multiplication. However, this version
\ first ensures that A is positive, so it can support signed numbers.
\
\ ******************************************************************************
.SQUA2
BPL SQUA \ If A > 0, jump to SQUA
EOR #&FF \ Otherwise we need to negate A for the SQUA algorithm
CLC \ to work, so we do this using two's complement, by
ADC #1 \ setting A = ~A + 1
.SQUA
STA Q \ Set Q = A and P = A
STA P \ Set P = A
LDA #0 \ Set A = 0 so we can start building the answer in A
LDY #8 \ Set up a counter in Y to count the 8 bits in P
LSR P \ Set P = P >> 1
\ and C flag = bit 0 of P
.SQL1
BCC SQ1 \ If C (i.e. the next bit from P) is set, do the
CLC \ addition for this bit of P:
ADC Q \
\ A = A + Q
.SQ1
ROR A \ Shift A right to catch the next digit of our result,
\ which the next ROR sticks into the left end of P while
\ also extracting the next bit of P
ROR P \ Add the overspill from shifting A to the right onto
\ the start of P, and shift P right to fetch the next
\ bit for the calculation into the C flag
DEY \ Decrement the loop counter
BNE SQL1 \ Loop back for the next bit until P has been rotated
\ all the way
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: PIX
\ Type: Subroutine
\ Category: Drawing pixels
\ Summary: Draw a single pixel at a specific coordinate
\
\ ------------------------------------------------------------------------------
\
\ Draw a pixel at screen coordinate (X + 128, A + 128). The routine uses the
\ same approach as the PIXEL routine in the main game code, except it plots a
\ single pixel from TWOS instead of a two pixel dash from TWOS2. This applies
\ to the top part of the screen (the monochrome mode 4 space view).
\
\ See the PIXEL routine in the main game code for more details.
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ X The signed screen x-coordinate of the pixel to draw,
\ from -128 to 127, to be plotted relative to the origin
\ at (128, 128)
\
\ A The signed screen y-coordinate of the pixel to draw,
\ from -128 to 127, to be plotted relative to the origin
\ at (128, 128)
\
\ ******************************************************************************
.PIX
TAY \ Copy A into Y, for use later
EOR #%10000000 \ Add 128 to A and treat this as an unsigned number from
\ now on
LSR A \ Set ZP+1 = &60 + A >> 3
LSR A
LSR A
ORA #&60
STA ZP+1
TXA \ Set A = X + 128 and treat this as an unsigned number
EOR #%10000000 \ from now on
AND #%11111000 \ Set ZP = (A >> 3) * 8
STA ZP
TYA \ Set Y = Y mod 8, which is the pixel row within the
AND #7 \ character block at which we want to draw our pixel
TAY \ (as each character block has 8 rows)
TXA \ Set X = X mod 8, which is the horizontal pixel number
AND #7 \ within the character block where the pixel lies (as
TAX \ each pixel line in the character block is 8 pixels
\ wide)
LDA TWOS,X \ Fetch a pixel from TWOS and OR it into ZP+Y
ORA (ZP),Y
STA (ZP),Y
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: TWOS
\ Type: Variable
\ Category: Drawing pixels
\ Summary: Ready-made single-pixel character row bytes for mode 4
\
\ ------------------------------------------------------------------------------
\
\ Ready-made bytes for plotting one-pixel points in mode 4 (the top part of the
\ split screen). See the PIX routine for details.
\
\ ******************************************************************************
.TWOS
EQUB %10000000
EQUB %01000000
EQUB %00100000
EQUB %00010000
EQUB %00001000
EQUB %00000100
EQUB %00000010
EQUB %00000001
\ ******************************************************************************
\
\ Name: CNT
\ Type: Variable
\ Category: Drawing planets
\ Summary: A counter for use in drawing Saturn's planetary body
\
\ ------------------------------------------------------------------------------
\
\ Defines the number of iterations of the PLL1 loop, which draws the planet part
\ of the loading screen's Saturn.
\
\ ******************************************************************************
.CNT
EQUW &0500 \ The number of iterations of the PLL1 loop (1280)
\ ******************************************************************************
\
\ Name: CNT2
\ Type: Variable
\ Category: Drawing planets
\ Summary: A counter for use in drawing Saturn's background stars
\
\ ------------------------------------------------------------------------------
\
\ Defines the number of iterations of the PLL2 loop, which draws the background
\ stars on the loading screen.
\
\ ******************************************************************************
.CNT2
EQUW &01DD \ The number of iterations of the PLL2 loop (477)
\ ******************************************************************************
\
\ Name: CNT3
\ Type: Variable
\ Category: Drawing planets
\ Summary: A counter for use in drawing Saturn's rings
\
\ ------------------------------------------------------------------------------
\
\ Defines the number of iterations of the PLL3 loop, which draws the rings
\ around the loading screen's Saturn.
\
\ ******************************************************************************
.CNT3
EQUW &0500 \ The number of iterations of the PLL3 loop (1280)
\ ******************************************************************************
\
\ Name: ROOT
\ Type: Subroutine
\ Category: Maths (Arithmetic)
\ Summary: Calculate ZP = SQRT(ZP(1 0))
\
\ ------------------------------------------------------------------------------
\
\ Calculate the following square root:
\
\ ZP = SQRT(ZP(1 0))
\
\ This routine is identical to LL5 in the main game code - it even has the same
\ label names. The only difference is that LL5 calculates Q = SQRT(R Q), but
\ apart from the variables used, the instructions are identical, so see the LL5
\ routine in the main game code for more details on the algorithm used here.
\
\ ******************************************************************************
.ROOT
LDY ZP+1 \ Set (Y Q) = ZP(1 0)
LDA ZP
STA Q
\ So now to calculate ZP = SQRT(Y Q)
LDX #0 \ Set X = 0, to hold the remainder
STX ZP \ Set ZP = 0, to hold the result
LDA #8 \ Set P = 8, to use as a loop counter
STA P
.LL6
CPX ZP \ If X < ZP, jump to LL7
BCC LL7
BNE LL8 \ If X > ZP, jump to LL8
CPY #64 \ If Y < 64, jump to LL7 with the C flag clear,
BCC LL7 \ otherwise fall through into LL8 with the C flag set
.LL8
TYA \ Set Y = Y - 64
SBC #64 \
TAY \ This subtraction will work as we know C is set from
\ the BCC above, and the result will not underflow as we
\ already checked that Y >= 64, so the C flag is also
\ set for the next subtraction
TXA \ Set X = X - ZP
SBC ZP
TAX
.LL7
ROL ZP \ Shift the result in Q to the left, shifting the C flag
\ into bit 0 and bit 7 into the C flag
ASL Q \ Shift the dividend in (Y S) to the left, inserting
TYA \ bit 7 from above into bit 0
ROL A
TAY
TXA \ Shift the remainder in X to the left
ROL A
TAX
ASL Q \ Shift the dividend in (Y S) to the left
TYA
ROL A
TAY
TXA \ Shift the remainder in X to the left
ROL A
TAX
DEC P \ Decrement the loop counter
BNE LL6 \ Loop back to LL6 until we have done 8 loops
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: BEGIN%
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Single-byte decryption and copying routine, run on the stack
\
\ ------------------------------------------------------------------------------
\
\ This routine is copied to the stack at &01F1. It pushes BLOCK to ENDBLOCK onto
\ the stack, and decrypts the code from TUT onwards.
\
\ The 15 instructions for this routine are pushed onto the stack and executed
\ there. The instructions are pushed onto the stack in reverse (as the stack
\ grows downwards in memory), so first the JMP gets pushed, then the STA, and
\ so on.
\
\ This is the code that is pushed onto the stack. It gets run by a JMP call to
\ David2, which then calls the routine on the stack with JSR &01F1.
\
\ 01F1 : PLA \ Remove the return address from the stack that was
\ 01F2 : PLA \ put here by the JSR that called this routine
\
\ 01F3 : LDA BLOCK,Y \ Set A = the Y-th byte of BLOCK
\
\ 01F6 : PHA \ Push A onto the stack
\
\ 01F7 : EOR TUT,Y \ EOR the Y-th byte of TUT with A
\ 01FA : STA TUT,Y
\
\ 01FD : JMP (David9) \ Jump to the address in David9
\
\ The routine is called inside a loop with Y as the counter. It counts from 0 to
\ ENDBLOCK - BLOCK, so the routine eventually pushes every byte between BLOCK
\ and ENDBLOCK onto the stack, as well as EOR'ing each byte from TUT onwards to
\ decrypt that section.
\
\ The elite-checksums.py script reverses the order of the bytes between BLOCK
\ and ENDBLOCK in the final file, so pushing them onto the stack (which is a
\ descending stack) realigns them in memory as assembled below. Not only that,
\ but the last two bytes pushed on the stack are the ones that are at the start
\ of the block at BLOCK, and these contain the address of ENTRY2. This is why
\ the RTS at the end of part 4 above actually jumps to ENTRY2 in part 5.
\
\ ******************************************************************************
.BEGIN%
EQUB HI(David9) \ JMP (David9)
EQUB LO(David9)
EQUB &6C
EQUB HI(TUT) \ STA TUT,Y
EQUB LO(TUT)
EQUB &99
IF _REMOVE_CHECKSUMS
EQUB HI(TUT) \ If we have disabled checksums, then just load the Y-th
EQUB LO(TUT) \ byte of TUT with LDA TUT,Y
EQUB &B9
ELSE
EQUB HI(TUT) \ EOR TUT,Y
EQUB LO(TUT)
EQUB &59
ENDIF
PHA \ PHA
EQUB HI(BLOCK) \ LDA BLOCK,Y
EQUB LO(BLOCK)
EQUB &B9
PLA \ PLA
PLA \ PLA
\ ******************************************************************************
\
\ Name: DOMOVE
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Multi-byte decryption and copying routine, run on the stack
\
\ ------------------------------------------------------------------------------
\
\ This routine is copied to the stack at &01DF. It moves and decrypts a block of
\ memory. The original source refers to the stack routine as MVDL.
\
\ The 18 instructions for this routine are pushed onto the stack and executed
\ there. The instructions are pushed onto the stack in reverse (as the stack
\ grows downwards in memory), so first the RTS gets pushed, then the BNE, and
\ so on.
\
\ This is the code that is pushed onto the stack. It gets run by a JMP call to
\ crunchit, which then calls the routine on the stack at MVDL, or &01DF. The
\ label MVDL comes from a comment in the original source file ELITES.
\
\ 01DF : .MVDL
\
\ 01DF : LDA (ZP),Y \ Set A = the Y-th byte from the block whose address
\ \ is in ZP(1 0)
\
\ 01E1 : EOR OSB,Y \ EOR A with the Y-th byte on from OSB
\
\ 01E4 : STA (P),Y \ Store A in the Y-th byte of the block whose
\ \ address is in P(1 0)
\
\ 01E6 : DEY \ Decrement the loop counter
\
\ 01E7 : BNE MVDL \ Loop back to copy and EOR the next byte until we
\ \ have copied an entire page (256 bytes)
\
\ 01E9 : INC P+1 \ Increment the high byte of P(1 0) so it points to
\ \ the next page of 256 bytes
\
\ 01EB : INC ZP+1 \ Increment ZP(1 0) so it points to the next page of
\ \ 256 bytes
\
\ 01ED : DEX \ Decrement X
\
\ 01EE : BNE MVDL \ Loop back to copy the next page
\
\ 01F0 : RTS \ Return from the subroutine, which takes us back
\ \ to the caller of the crunchit routine using a
\ \ tail call, as we called this with JMP crunchit
\
\ We call MVDL with the following arguments:
\
\ (X Y) The number of bytes to copy
\
\ ZP(1 0) The source address
\
\ P(1 0) The destination address
\
\ The routine moves and decrypts a block of memory, and is used in part 3 to
\ move blocks of code and images that are embedded within the loader binary,
\ either into low memory locations below PAGE (for the recursive token table and
\ page at UU%), or into screen memory (for the loading screen and dashboard
\ images).
\
\ If checksums are disabled in the build, we don't do the EOR instruction, so
\ the routine just moves and doesn't decrypt.
\
\ ******************************************************************************
.DOMOVE
RTS \ RTS
EQUW &D0EF \ BNE MVDL
DEX \ DEX
EQUB ZP+1 \ INC ZP+1
INC P+1 \ INC P+1
EQUB &E6
EQUW &D0F6 \ BNE MVDL
DEY \ DEY
EQUB P \ STA(P),Y
EQUB &91
IF _REMOVE_CHECKSUMS
NOP \ If we have disabled checksums, skip the EOR so the
NOP \ routine just does the copying part
NOP
ELSE
EQUB HI(OSB) \ EOR OSB,Y
EQUB LO(OSB)
EQUB &59
ENDIF
EQUB ZP \ LDA(ZP),Y
EQUB &B1
\ ******************************************************************************
\
\ Name: UU%
\ Type: Workspace
\ Address: &0B00
\ Category: Workspaces
\ Summary: Marker for a block that is moved as part of the obfuscation
\
\ ------------------------------------------------------------------------------
\
\ The code from here to the end of the file gets copied to &0B00 (LE%) by part
\ 3. It is called from the end of part 4, via ENTRY2 in part 5 below.
\
\ ******************************************************************************
.UU%
Q% = P% - LE%
ORG LE% \ Set the assembly address to LE%
\ ******************************************************************************
\
\ Name: CHECKbyt
\ Type: Variable
\ Category: Copy protection
\ Summary: Checksum for the validity of the UU% workspace
\
\ ------------------------------------------------------------------------------
\
\ We calculate the value of the CHECKbyt checksum in elite-checksum.py, so this
\ just reserves a byte. It checks the validity of the first two pages of the UU%
\ workspace, which gets copied to LE%.
\
\ ******************************************************************************
.CHECKbyt
BRK \ This could be an EQUB 0 directive instead of a BRK,
\ but this is what's in the source code
\ ******************************************************************************
\
\ Name: MAINSUM
\ Type: Variable
\ Category: Copy protection
\ Summary: Two checksums for the decryption header and text token table
\
\ ------------------------------------------------------------------------------
\
\ Contains two checksum values, one for the header code at LBL, and the other
\ for the recursive token table from &0400 to &07FF.
\
\ ******************************************************************************
.MAINSUM
EQUB &CB \ This is the checksum value of the decryption header
\ code (from LBL to elitea) that gets prepended to the
\ main game code by elite-bcfs.asm and saved as
\ ELThead.bin
EQUB 0 \ This is the checksum value for the recursive token
\ table from &0400 to &07FF. We calculate the value in
\ elite-checksum.py, so this just reserves a byte
\ ******************************************************************************
\
\ Name: FOOLV
\ Type: Variable
\ Category: Copy protection
\ Summary: Part of the AFOOL roundabout obfuscation routine
\
\ ------------------------------------------------------------------------------
\
\ FOOLV contains the address of FOOL. This is part of the JSR AFOOL obfuscation
\ routine, which calls AFOOL, which then jumps to the address in FOOLV, which
\ contains the address of FOOL, which contains an RTS instruction... so overall
\ it does nothing, but in a rather roundabout fashion.
\
\ ******************************************************************************
.FOOLV
EQUW FOOL \ The address of FOOL, which contains an RTS
\ ******************************************************************************
\
\ Name: CHECKV
\ Type: Variable
\ Category: Copy protection
\ Summary: The address of the LBL routine in the decryption header
\
\ ------------------------------------------------------------------------------
\
\ CHECKV contains the address of the LBL routine at the very start of the main
\ game code file, in the decryption header code that gets prepended to the main
\ game code by elite-bcfs.asm and saved as ELThead.bin
\
\ ******************************************************************************
.CHECKV
EQUW LOAD%+1 \ The address of the LBL routine
\ ******************************************************************************
\
\ Name: block1
\ Type: Variable
\ Category: Drawing the screen
\ Summary: Palette data for the two dashboard colour scheme
\
\ ------------------------------------------------------------------------------
\
\ Palette bytes for use with the split-screen mode 5. See TVT1 in the main game
\ code for an explanation.
\
\ ******************************************************************************
.block1
EQUB &F5, &E5
EQUB &B5, &A5
EQUB &76, &66
EQUB &36, &26
EQUB &D4, &C4
EQUB &94, &84
\ ******************************************************************************
\
\ Name: block2
\ Type: Variable
\ Category: Drawing the screen
\ Summary: Palette data for the space part of the screen
\
\ ------------------------------------------------------------------------------
\
\ Palette bytes for use with the split-screen mode 4. See TVT1 in the main game
\ code for an explanation.
\
\ ******************************************************************************
.block2
EQUB &D0, &C0
EQUB &B0, &A0
EQUB &F0, &E0
EQUB &90, &80
EQUB &77, &67
EQUB &37, &27
\ ******************************************************************************
\
\ Name: TT26
\ Type: Subroutine
\ Category: Text
\ Summary: Print a character at the text cursor (WRCHV points here)
\
\ ------------------------------------------------------------------------------
\
\ This routine prints a character at the text cursor (XC, YC). It is very
\ similar to the routine of the same name in the main game code, so refer to
\ that routine for a more detailed description.
\
\ This routine, however, only works within a small 14x14 character text window,
\ which we use for the tape loading messages, so there is extra code for fitting
\ the text into the window (and it also reverses the effect of line feeds and
\ carriage returns).
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ A The character to be printed
\
\ XC Contains the text column to print at (the x-coordinate)
\
\ YC Contains the line number to print on (the y-coordinate)
\
\ ------------------------------------------------------------------------------
\
\ Returns:
\
\ A A is preserved
\
\ X X is preserved
\
\ Y Y is preserved
\
\ ******************************************************************************
.TT26
STA K3 \ Store the A, X and Y registers (in K3 for A, and on
TYA \ the stack for the others), so we can restore them at
PHA \ the end (so they don't get changed by this routine)
TXA
PHA
.rr
LDA K3 \ Set A = the character to be printed
CMP #7 \ If this is a beep character (A = 7), jump to R5,
BEQ R5 \ which will emit the beep, restore the registers and
\ return from the subroutine
CMP #32 \ If this is an ASCII character (A >= 32), jump to RR1
BCS RR1 \ below, which will print the character, restore the
\ registers and return from the subroutine
CMP #13 \ If this is control code 13 (carriage return) then jump
BEQ RRX1 \ to RRX1, which will move along on character, restore
\ the registers and return from the subroutine (as we
\ don't have room in the text window for new lines)
INC YC \ If we get here, then this is control code 10, a line
\ feed, so move down one line and fall through into RRX1
\ to move the cursor to the start of the line
.RRX1
LDX #7 \ Set the column number (x-coordinate) of the text
STX XC \ to 7
BNE RR4 \ Jump to RR4 to restore the registers and return from
\ the subroutine (this BNE is effectively a JMP as Y
\ will never be zero)
.RR1
LDX #&BF \ Set X to point to the first font page in ROM minus 1,
\ which is &C0 - 1, or &BF
ASL A \ If bit 6 of the character is clear (A is 32-63)
ASL A \ then skip the following instruction
BCC P%+4
LDX #&C1 \ A is 64-126, so set X to point to page &C1
ASL A \ If bit 5 of the character is clear (A is 64-95)
BCC P%+3 \ then skip the following instruction
INX \ Increment X, so X now contains the high byte
\ (the page) of the address of the definition that we
\ want, while A contains the low byte (the offset into
\ the page) of the address
STA P \ Store the address of this character's definition in
STX P+1 \ P(1 0)
LDA XC \ If the column number (x-coordinate) of the text is
CMP #20 \ less than 20, skip to NOLF
BCC NOLF
LDA #7 \ Otherwise we just reached the end of the line, so
STA XC \ move the text cursor to column 7, and down onto the
INC YC \ next line
.NOLF
ASL A \ Multiply the x-coordinate (column) of the text by 8
ASL A \ and store in ZP, to get the low byte of the screen
ASL A \ address for the character we want to print
STA ZP
INC XC \ Once we print the character, we want to move the text
\ cursor to the right, so we do this by incrementing XC
LDA YC \ If the row number (y-coordinate) of the text is less
CMP #19 \ than 19, skip to RR3
BCC RR3
\ Otherwise we just reached the bottom of the screen,
\ which is a small 14x14 character text window we use
\ for showing the tape loading messages, so now we need
\ to clear that window and move the cursor to the top
LDA #7 \ Move the text cursor to column 7
STA XC
LDA #&65 \ Set the high byte of the SC(1 0) to &65, for character
STA SC+1 \ row 5 of the screen
LDY #7*8 \ Set Y = 7 * 8, for column 7 (as there are 8 bytes per
\ character block)
LDX #14 \ Set X = 14, to count the number of character rows we
\ need to clear
STY SC \ Set the low byte of SC(1 0) to 7*8, so SC(1 0) now
\ points to the character block at row 5, column 7, at
\ the top-left corner of the small text window
LDA #0 \ Set A = 0 for use in clearing the screen (which we do
\ by setting the screen memory to 0)
TAY \ Set Y = 0
.David1
STA (SC),Y \ Clear the Y-th byte of the block pointed to by SC(1 0)
INY \ Increment the counter in Y
CPY #14*8 \ Loop back to clear the next byte until we have done 14
BCC David1 \ lots of 8 bytes (i.e. 14 characters, the width of the
\ small text window)
TAY \ Set Y = 0, ready for the next row
INC SC+1 \ Point SC(1 0) to the next page in memory, i.e. the
\ next character row
DEX \ Decrement the counter in X
BPL David1 \ Loop back to David1 until we have done 14 character
\ rows (the height of the small text window)
LDA #5 \ Move the text cursor to row 5
STA YC
BNE rr \ Jump to rr to print the character we were about to
\ print when we ran out of space (this BNE is
\ effectively a JMP as A will never be zero)
.RR3
ORA #&60 \ Add &60 to YC, giving us the page number that we want
STA ZP+1 \ Store the page number of the destination screen
\ location in ZP+1, so ZP now points to the full screen
\ location where this character should go
LDY #7 \ We want to print the 8 bytes of character data to the
\ screen (one byte per row), so set up a counter in Y
\ to count these bytes
.RRL1
LDA (P),Y \ The character definition is at P(1 0) - we set this up
\ above - so load the Y-th byte from P(1 0)
STA (ZP),Y \ Store the Y-th byte at the screen address for this
\ character location
DEY \ Decrement the loop counter
BPL RRL1 \ Loop back for the next byte to print to the screen
.RR4
PLA \ We're done printing, so restore the values of the
TAX \ A, X and Y registers that we saved above, loading them
PLA \ from K3 (for A) and the stack (for X and Y)
TAY
LDA K3
.FOOL
RTS \ Return from the subroutine
.R5
LDA #7 \ Control code 7 makes a beep, so load this into A
JSR osprint \ Call OSPRINT to "print" the beep character
JMP RR4 \ Jump to RR4 to restore the registers and return from
\ the subroutine using a tail call
\ ******************************************************************************
\
\ Name: osprint
\ Type: Subroutine
\ Category: Utility routines
\ Summary: Print a character
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ A The character to print
\
\ ******************************************************************************
.TUT
.osprint
JMP (OSPRNT) \ Jump to the address in OSPRNT and return using a
\ tail call
EQUB &6C \ This byte appears to be unused
\ ******************************************************************************
\
\ Name: command
\ Type: Subroutine
\ Category: Utility routines
\ Summary: Execute an OS command
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ (Y X) The address of the OS command string to execute
\
\ ******************************************************************************
.command
JMP (oscliv) \ Jump to &FFF7 to execute the OS command pointed to
\ by (Y X) and return using a tail call
\ ******************************************************************************
\
\ Name: MESS1
\ Type: Variable
\ Category: Utility routines
\ Summary: Contains an OS command string for loading the main game code
\
\ ******************************************************************************
.MESS1
IF DISC
EQUS "L.ELTcode 1100" \ This is short for "*LOAD ELTcode 1100"
ELSE
EQUS "L.ELITEcode F1F" \ This is short for "*LOAD ELITEcode F1F"
ENDIF
EQUB 13
\ ******************************************************************************
\
\ Name: Elite loader (Part 5 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Load main game code, decrypt it, move it to the correct location
\
\ ------------------------------------------------------------------------------
\
\ This part loads the main game code, decrypts it and moves it to the correct
\ location for it to run.
\
\ The code in this part is encrypted by elite-checksum.py and is decrypted in
\ part 4 by the same routine that moves part 6 onto the stack.
\
\ ******************************************************************************
.ENTRY2
\ We start this part of the loader by setting the
\ following:
\
\ OSPRNT(1 0) = WRCHV
\ WRCHV(1 0) = TT26
\ (Y X) = MESS1(1 0)
\
\ so any character printing will use the TT26 routine
LDA &020E \ Copy the low byte of WRCHV to the low byte of OSPRNT
STA OSPRNT
LDA #LO(TT26) \ Set the low byte of WRCHV to the low byte of TT26
STA &020E
LDX #LO(MESS1) \ Set X to the low byte of MESS1
LDA &020F \ Copy the high byte of WRCHV to the high byte of OSPRNT
STA OSPRNT+1
LDA #HI(TT26) \ Set the high byte of WRCHV to the high byte of TT26
LDY #HI(MESS1) \ and set Y to the high byte of MESS1
STA &020F
JSR AFOOL \ This calls AFOOL, which jumps to the address in FOOLV,
\ which contains the address of FOOL, which contains an
\ RTS instruction... so overall this does nothing, but
\ in a rather roundabout fashion
JSR command \ Call command to execute the OSCLI command pointed to
\ by (Y X) in MESS1, which starts loading the main game
\ code
JSR 512-LEN+CHECKER-ENDBLOCK \ Call the CHECKER routine in its new location on
\ the stack, to run a number of checksums on the
\ code (this routine, along with the whole of part
\ 6, was pushed onto the stack in part 4)
JSR AFOOL \ Another call to the round-the-houses routine to try
\ and distract the crackers, presumably
IF DISC
LDA #140 \ Call OSBYTE with A = 140 and X = 12 to select the
LDX #12 \ tape filing system (i.e. do a *TAPE command)
JSR OSBYTE
ENDIF
LDA #0 \ Set SVN to 0, as the main game code checks the value
STA SVN \ of this location in its IRQ1 routine, so it needs to
\ be set to 0 so it can work properly once it takes over
\ when the game itself runs
\ We now decrypt and move the main game code from &1128
\ to &0F40
LDX #HI(LC%) \ Set X = high byte of LC%, the maximum size of the main
\ game code, so if we move this number of pages, we will
\ have definitely moved all the game code down
LDA #LO(L%) \ Set ZP(1 0) = L% (the start of the game code)
STA ZP
LDA #HI(L%)
STA ZP+1
LDA #LO(C%) \ Set P(1 0) = C% = &0F40
STA P
LDA #HI(C%)
STA P+1
LDY #0 \ Set Y as a counter for working our way through every
\ byte of the game code. We EOR the counter with the
\ current byte to decrypt it
.ML1
TYA \ Copy the counter into A
IF _REMOVE_CHECKSUMS
LDA (ZP),Y \ If we have disabled checksums, just fetch the byte to
\ copy from the Y-th block pointed to by ZP(1 0)
ELSE
EOR (ZP),Y \ Fetch the byte and EOR it with the counter
ENDIF
STA (P),Y \ Store the copied (and decrypted) byte in the Y-th byte
\ of the block pointed to by P(1 0)
INY \ Increment the loop counter
BNE ML1 \ Loop back for the next byte until we have finished the
\ first 256 bytes
INC ZP+1 \ Increment the high bytes of both ZP(1 0) and P(1 0) to
INC P+1 \ point to the next 256 bytes
DEX \ Decrement the number of pages we need to copy in X
BPL ML1 \ Loop back to copy and decrypt the next page of bytes
\ until we have done them all
\ S% points to the entry point for the main game code,
\ so the following copies the addresses from the start
\ of the main code (see the S% label in the main game
\ code for the vector values)
LDA S%+6 \ Set BRKV to point to the BR1 routine in the main game
STA &0202 \ code
LDA S%+7
STA &0203
LDA S%+2 \ Set WRCHV to point to the TT26 routine in the main
STA &020E \ game code
LDA S%+3
STA &020F
RTS \ This RTS actually does a jump to the first instruction
\ in BLOCK, after the two EQUW operatives, which is now
\ on the stack. This takes us to the next and final
\ step of the loader in part 6. See the documentation
\ for the stack routine at BEGIN% for more details
.AFOOL
JMP (FOOLV) \ This jumps to the address in FOOLV as part of the
\ JSR AFOOL instruction above, which does nothing except
\ take us on wild goose chase
\ ******************************************************************************
\
\ Name: M2
\ Type: Variable
\ Category: Utility routines
\ Summary: Used for testing the 6522 System VIA status byte in IRQ1
\
\ ------------------------------------------------------------------------------
\
\ Used for testing bit 1 of the 6522 System VIA status byte in the IRQ1 routine,
\ as well as bit 1 of the block flag.
\
\ ******************************************************************************
.M2
EQUB %00000010 \ Bit 1 is set
\ ******************************************************************************
\
\ Name: IRQ1
\ Type: Subroutine
\ Category: Drawing the screen
\ Summary: The loader's screen-mode interrupt handler (IRQ1V points here)
\ Deep dive: The split-screen mode in BBC Micro Elite
\
\ ------------------------------------------------------------------------------
\
\ The main interrupt handler, which implements Elite's split-screen mode.
\
\ This routine is similar to the main IRQ1 routine in the main game code, except
\ it's a bit simpler (it doesn't need to support the mode-flashing effect of
\ hyperspace, for example).
\
\ It also sets Timer 1 to a different value, 14386 instead of 14622. The split
\ in the split-screen mode does overlap more in the loader than in the game, so
\ it's interesting that they didn't fine-tune this version as much.
\
\ For more details on how the following works, see the IRQ1 routine in the main
\ game code.
\
\ ******************************************************************************
.VIA2
LDA #%00000100 \ Set the Video ULA control register (SHEILA &20) to
STA &FE20 \ %00000100, which is the same as switching to mode 5,
\ (i.e. the bottom part of the screen) but with no
\ cursor
LDY #11 \ We now apply the palette bytes from block1 to the
\ mode 5 screen, so set a counter in Y for 12 bytes
.inlp1
LDA block1,Y \ Copy the Y-th palette byte from block1 to SHEILA &21
STA &FE21 \ to map logical to actual colours for the bottom part
\ of the screen (i.e. the dashboard)
DEY \ Decrement the palette byte counter
BPL inlp1 \ Loop back to the inlp1 until we have copied all the
\ palette bytes
PLA \ Restore Y from the stack
TAY
JMP (VEC) \ Jump to the address in VEC, which was set to the
\ original IRQ1V vector in part 4, so this instruction
\ passes control to the next interrupt handler
.IRQ1
TYA \ Store Y on the stack
PHA
IF PROT AND NOT(DISC)
\ By this point, we have set up the following in
\ various places throughout the loader code (such as
\ part 2 and PLL1):
\
\ BLPTR(1 0) = &03CA
\ BLN(1 0) = &03C6
\ EXCN(1 0) = &03C2
\
\ BLPTR (&03CA) is a byte in the MOS workspace that
\ stores the block flag of the most recent block loaded
\ from tape
\
\ BLN (&03C6) is the low byte of the number of the last
\ block loaded from tape
\
\ EXCN (&03C2) is the low byte of the execution address
\ of the file being loaded
LDY #0 \ Set A to the block flag of the most recent block
LDA (BLPTR),Y \ loaded from tape
BIT M2 \ If bit 1 of the block flag is set, jump to itdone
BNE itdone
EOR #%10000011 \ Otherwise flip bits 0, 1 and 7 of A. This has two
\ main effects:
\
\ * Bit 0 of the block flag gets cleared. Most
\ cassette versions of Acornsoft games are saved to
\ tape with locked blocks, so you can't just load
\ the game into memory (you'll get a "Locked" error
\ for each block). Locked blocks have bit 0 set, so
\ this clears the locked status, so when the MOS
\ gets round to checking whether the block is
\ locked, we've already cleared it and updated it in
\ memory (which we do below), so the block loads
\ without throwing an error
\
\ * Bit 1 of the block flag gets set, so we won't
\ increment BLCNT again until the next block starts
\ loading (so in this way we count the number of
\ blocks loaded in BLCNT)
INC BLCNT \ Increment BLCNT, which was initialised to 0 in part 3
BNE ZQK \ If BLCNT is non-zero, skip the next instruction
DEC BLCNT \ If incrementing BLCNT set it to zero, decrement it, so
\ this sets a maximum of 255 on BLCNT
.ZQK
STA (BLPTR),Y \ Store the updated value of A in the block flag, so the
\ block gets unlocked
LDA #35 \ If the block number in BLN is 35, skip the next
CMP (BLN),Y \ instruction, leaving A = 32 = &23
BEQ P%+4
EOR #17 \ Set A = 35 EOR 17 = 50 = &32
CMP (EXCN),Y \ If the low byte of the execution address of the file
BEQ itdone \ we are loading is equal to A (which is either &23 or
\ &32), skip to itdone
DEC LOAD% \ Otherwise decrement LOAD%, which is the address of the
\ first byte of the main game code file (i.e. the load
\ address of "ELTcode"), so this decrements the first
\ byte of the file we are loading, i.e. the LBL variable
\ added by the Big Code File source
.itdone
ENDIF
LDA VIA+&4D \ Read the 6522 System VIA status byte bit 1 (SHEILA
BIT M2 \ &4D), which is set if vertical sync has occurred on
\ the video system
BNE LINSCN \ If we are on the vertical sync pulse, jump to LINSCN
\ to set up the timers to enable us to switch the
\ screen mode between the space view and dashboard
AND #%01000000 \ If the 6522 System VIA status byte bit 6 is set, which
BNE VIA2 \ means timer 1 has timed out, jump to VIA2
PLA \ Restore Y from the stack
TAY
JMP (VEC) \ Jump to the address in VEC, which was set to the
\ original IRQ1V vector in part 4, so this instruction
\ passes control to the next interrupt handler
.LINSCN
LDA #50 \ Set 6522 System VIA T1C-L timer 1 low-order counter
STA VIA+&44 \ (SHEILA &44) to 50
LDA #VSCAN \ Set 6522 System VIA T1C-L timer 1 high-order counter
STA VIA+&45 \ (SHEILA &45) to VSCAN (56) to start the T1 counter
\ counting down from 14386 at a rate of 1 MHz
LDA #8 \ Set the Video ULA control register (SHEILA &20) to
STA &FE20 \ %00001000, which is the same as switching to mode 4
\ (i.e. the top part of the screen) but with no cursor
LDY #11 \ We now apply the palette bytes from block2 to the
\ mode 4 screen, so set a counter in Y for 12 bytes
.inlp2
LDA block2,Y \ Copy the Y-th palette byte from block2 to SHEILA &21
STA &FE21 \ to map logical to actual colours for the top part of
\ the screen (i.e. the space view)
DEY \ Decrement the palette byte counter
BPL inlp2 \ Loop back to the inlp1 until we have copied all the
\ palette bytes
PLA \ Restore Y from the stack
TAY
JMP (VEC) \ Jump to the address in VEC, which was set to the
\ original IRQ1V vector in part 4, so this instruction
\ passes control to the next interrupt handler
\ ******************************************************************************
\
\ Name: BLOCK
\ Type: Variable
\ Category: Copy protection
\ Summary: Addresses for the obfuscated jumps that use RTS not JMP
\
\ ------------------------------------------------------------------------------
\
\ These two addresses get pushed onto the stack in part 4. The first EQUW is the
\ address of ENTRY2, while the second is the address of the first instruction in
\ part 6, after it is pushed onto the stack.
\
\ This entire section from BLOCK to ENDBLOCK gets copied into the stack at
\ location &015E by part 4, so by the time we call the routine at the second
\ EQUW address at the start, the entry point is on the stack at &0163.
\
\ This means that the RTS instructions at the end of parts 4 and 5 jump to
\ ENTRY2 and the start of part 6 respectively. See part 4 for details.
\
\ ******************************************************************************
.BLOCK
EQUW ENTRY2-1
EQUW 512-LEN+BLOCK-ENDBLOCK+3
\ ******************************************************************************
\
\ Name: Elite loader (Part 6 of 6)
\ Type: Subroutine
\ Category: Loader
\ Summary: Set up interrupt vectors, calculate checksums, run main game code
\
\ ------------------------------------------------------------------------------
\
\ This is the final part of the loader. It sets up some of the main game's
\ interrupt vectors and calculates various checksums, before finally handing
\ over to the main game.
\
\ ------------------------------------------------------------------------------
\
\ Other entry points:
\
\ nononono Reset the machine
\
\ ******************************************************************************
LDA VIA+&44 \ Read the 6522 System VIA T1C-L timer 1 low-order
STA &0001 \ counter (SHEILA &44), which decrements one million
\ times a second and will therefore be pretty random,
\ and store it in location &0001, which is among the
\ main game code's random seeds (so this seeds the
\ random number generator for the main game)
SEI \ Disable all interrupts
LDA #%00111001 \ Set 6522 System VIA interrupt enable register IER
STA VIA+&4E \ (SHEILA &4E) bits 0 and 3-5 (i.e. disable the Timer1,
\ CB1, CB2 and CA2 interrupts from the System VIA)
\LDA #&7F \ These instructions are commented out in the original
\STA &FE6E \ source with the comment "already done", which they
\LDA IRQ1V \ were, in part 4
\STA VEC
\LDA IRQ1V+1
\STA VEC+1
LDA S%+4 \ S% points to the entry point for the main game code,
STA IRQ1V \ so this copies the address of the main game's IRQ1
LDA S%+5 \ routine from the start of the main code into IRQ1V
STA IRQ1V+1
LDA #VSCAN \ Set 6522 System VIA T1C-L timer 1 high-order counter
STA VIA+&45 \ (SHEILA &45) to VSCAN (56) to start the T1 counter
\ counting down from 14080 at a rate of 1 MHz (this is
\ a different value to the main game code)
CLI \ Re-enable interrupts
\LDA #129 \ These instructions are commented out in the original
\LDY #&FF \ source. They call OSBYTE with A = 129, X = 1 and
\LDX #1 \ Y = &FF, which returns the machine type in X, so
\JSR OSBYTE \ this code would detect the MOS version
\
\TXA
\EOR #&FF
\STA MOS
\
\BMI BLAST
LDY #0 \ Call OSBYTE with A = 200, X = 3 and Y = 0 to disable
LDA #200 \ the ESCAPE key and clear memory if the BREAK key is
LDX #3 \ pressed
JSR OSBYTE
\ The rest of the routine calculates various checksums
\ and makes sure they are correct before proceeding, to
\ prevent code tampering. We start by calculating the
\ checksum for the main game code from &0F40 to &5540,
\ which just adds up every byte and checks it against
\ the checksum stored at the end of the main game code
.BLAST
LDA #HI(S%) \ Set ZP(1 0) = S%
STA ZP+1 \
LDA #LO(S%) \ so ZP(1 0) points to the start of the main game code
STA ZP
LDX #&45 \ We are going to checksum &45 pages from &0F40 to &5540
\ so set a page counter in X
LDY #0 \ Set Y to count through each byte within each page
TYA \ Set A = 0 for building the checksum
.CHK
CLC \ Add the Y-th byte of this page of the game code to A
ADC (ZP),Y
INY \ Increment the counter for this page
BNE CHK \ Loop back for the next byte until we have finished
\ adding up this page
INC ZP+1 \ Increment the high byte of ZP(1 0) to point to the
\ next page
DEX \ Decrement the page counter we set in X
BPL CHK \ Loop back to add up the next page until we have done
\ them all
IF _REMOVE_CHECKSUMS
LDA #0 \ If we have disabled checksums, just set A to 0 so the
NOP \ BEQ below jumps to itsOK
ELSE
CMP D%-1 \ D% is set to the address of the byte after the end of
\ the code, so this compares the result to the last byte
\ in the main game code at location checksum0
ENDIF
BEQ itsOK \ If the checksum we just calculated matches the value
\ in location checksum0, jump to itsOK
.nononono
STA S%+1 \ If we get here then the checksum was wrong, so first
\ we store the incorrect checksum value in the low byte
\ of the address stored at the start of the main game
\ code, which contains the address of TT170, the entry
\ point for the main game (so this hides this address
\ from prying eyes)
LDA #%01111111 \ Set 6522 System VIA interrupt enable register IER
STA &FE4E \ (SHEILA &4E) bits 0-6 (i.e. disable all hardware
\ interrupts from the System VIA)
JMP (&FFFC) \ Jump to the address in &FFFC to reset the machine
.itsOK
JMP (S%) \ The checksum was correct, so we call the address held
\ in the first two bytes of the main game code, which
\ point to TT170, the entry point for the main game
\ code, so this, finally, is where we hand over to the
\ game itself
\ ******************************************************************************
\
\ Name: CHECKER
\ Type: Subroutine
\ Category: Copy protection
\ Summary: Run checksum checks on tokens, loader and tape block count
\
\ ------------------------------------------------------------------------------
\
\ This routine runs checksum checks on the recursive token table and the loader
\ code at the start of the main game code file, to prevent tampering with these
\ areas of memory. It also runs a check on the tape loading block count.
\
\ ------------------------------------------------------------------------------
\
\ Other entry points:
\
\ ENDBLOCK Denotes the end of the encrypted code that starts at
\ BLOCK
\
\ ******************************************************************************
.CHECKER
\ First we check the MAINSUM+1 checksum for the
\ recursive token table from &0400 to &07FF
LDY #0 \ Set Y = 0 to count through each byte within each page
LDX #4 \ We are going to checksum 4 pages from &0400 to &07FF
\ so set a page counter in X
STX ZP+1 \ Set ZP(1 0) = &0400, to point to the start of the code
STY ZP \ we want to checksum
TYA \ Set A = 0 for building the checksum
.CHKq
CLC \ Add the Y-th byte of this page of the token table to A
ADC (ZP),Y
INY \ Increment the counter for this page
BNE CHKq \ Loop back for the next byte until we have finished
\ adding up this page
INC ZP+1 \ Increment the high byte of ZP(1 0) to point to the
\ next page
DEX \ Decrement the page counter we set in X
BNE CHKq \ Loop back to add up the next page until we have done
\ them all
CMP MAINSUM+1 \ Compare the result to the contents of MAINSUM+1, which
\ contains the checksum for the table (this gets set by
\ elite-checksum.py)
IF _REMOVE_CHECKSUMS
NOP \ If we have disabled checksums, do nothing
NOP
ELSE
BNE nononono \ If the checksum we just calculated does not match the
\ contents of MAINSUM+1, jump to nononono to reset the
\ machine
ENDIF
\ Next, we check the LBL routine in the header that's
\ appended to the main game code in elite-bcfs.asm, and
\ which is currently loaded at LOAD% (which contains the
\ load address of the main game code file)
TYA \ Set A = 0 for building the checksum (as Y is still 0
\ from the above checksum loop)
.CHKb
CLC \ Add the Y-th byte of LOAD% to A
ADC LOAD%,Y
INY \ Increment the counter
CPY #40 \ There are 40 bytes in the loader, so loop back until
BNE CHKb \ we have added them all
CMP MAINSUM \ Compare the result to the contents of MAINSUM, which
\ contains the checksum for loader code
IF _REMOVE_CHECKSUMS
NOP \ If we have disabled checksums, do nothing
NOP
ELSE
BNE nononono \ If the checksum we just calculated does not match the
\ contents of MAINSUM, jump to nononono to reset the
\ machine
ENDIF
\ Finally, we check the block count from the tape
\ loading code in the IRQ1 routine, which counts the
\ number of blocks in the main game code
IF PROT AND NOT(DISC)
LDA BLCNT \ If the tape protection is enabled and we are loading
CMP #&4F \ from tape (as opposed to disc), check that the block
BCC nononono \ count in BLCNT is &4F, and if it isn't, jump to
\ nononono to reset the machine
ENDIF
IF _REMOVE_CHECKSUMS
RTS \ If we have disabled checksums, return from the
NOP \ subroutine
NOP
ELSE
JMP (CHECKV) \ Call the LBL routine in the header (whose address is
\ in CHECKV). This routine is inserted before the main
\ game code by elite-bcfs.asm, and it checks the
\ validity of the first two pages of the UU% routine,
\ which was copied to LE% above, and which contains a
\ checksum byte in CHECKbyt. We then return from the
\ subroutine using a tail call
ENDIF
.ENDBLOCK
\ ******************************************************************************
\
\ Name: XC
\ Type: Variable
\ Category: Text
\ Summary: The x-coordinate of the text cursor
\
\ ------------------------------------------------------------------------------
\
\ Contains the x-coordinate of the text cursor (i.e. the text column) with an
\ initial value of column 7, at the top-left corner of the 14x14 text window
\ where we show the tape loading messages (see TT26 for details).
\
\ ******************************************************************************
.XC
EQUB 7
\ ******************************************************************************
\
\ Name: YC
\ Type: Variable
\ Category: Text
\ Summary: The y-coordinate of the text cursor
\
\ ------------------------------------------------------------------------------
\
\ Contains the y-coordinate of the text cursor (i.e. the text row) with an
\ initial value of row 6, at the top-left corner of the 14x14 text window where
\ we show the tape loading messages (see TT26 for details).
\
\ ******************************************************************************
.YC
EQUB 6
\ ******************************************************************************
\
\ Save ELITE.unprot.bin
\
\ ******************************************************************************
COPYBLOCK LE%, P%, UU% \ Copy the block that we assembled at LE% to
\ UU%, which is where it will actually run
PRINT "Addresses for the scramble routines in elite-checksum.py"
PRINT "BLOCK_offset = ", ~(BLOCK - LE%) + (UU% - CODE%)
PRINT "ENDBLOCK_offset = ", ~(ENDBLOCK - LE%) + (UU% - CODE%)
PRINT "MAINSUM_offset = ", ~(MAINSUM - LE%) + (UU% - CODE%)
PRINT "TUT_offset = ", ~(TUT - LE%) + (UU% - CODE%)
PRINT "CHECKbyt_offset = ", ~(CHECKbyt - LE%) + (UU% - CODE%)
PRINT "CODE_offset = ", ~(OSB - CODE%)
PRINT "UU% = ", ~UU%
PRINT "Q% = ", ~Q%
PRINT "OSB = ", ~OSB
PRINT "Memory usage: ", ~LE%, " - ", ~P%
PRINT "Stack: ",LEN + ENDBLOCK - BLOCK
PRINT "S. ELITE ", ~CODE%, " ", ~UU% + (P% - LE%), " ", ~run, " ", ~CODE%
SAVE "3-assembled-output/ELITE.unprot.bin", CODE%, UU% + (P% - LE%), run, CODE%
================================================
FILE: 1-source-files/main-sources/elite-readme.asm
================================================
\ ******************************************************************************
\
\ BBC MICRO CASSETTE ELITE README SOURCE
\
\ BBC Micro cassette Elite was written by Ian Bell and David Braben and is
\ copyright Acornsoft 1984
\
\ The code in this file is identical to the source discs released on Ian Bell's
\ personal website at http://www.elitehomepage.org/ (it's just been reformatted
\ to be more readable)
\
\ 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://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file produces a README file for BBC Micro cassette Elite.
\
\ ------------------------------------------------------------------------------
\
\ This source file produces the following binary file:
\
\ * README.txt
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_SOURCE_DISC = (_VARIANT = 1)
_TEXT_SOURCES = (_VARIANT = 2)
_STH_CASSETTE = (_VARIANT = 3)
.readme
EQUB 10, 13
EQUS "---------------------------------------"
EQUB 10, 13
EQUS "Acornsoft Elite"
EQUB 10, 13
EQUB 10, 13
EQUS "Version: BBC Micro cassette"
EQUB 10, 13
IF _SOURCE_DISC
EQUS "Variant: Ian Bell's source disc"
EQUB 10, 13
ELIF _TEXT_SOURCES
EQUS "Variant: Ian Bell's text sources"
EQUB 10, 13
ELIF _STH_CASSETTE
EQUS "Variant: Stairway to Hell cassette"
EQUB 10, 13
EQUS "Product: Acornsoft SBG38"
EQUB 10, 13
ENDIF
EQUB 10, 13
EQUS "See www.bbcelite.com for details"
EQUB 10, 13
EQUS "---------------------------------------"
EQUB 10, 13
SAVE "3-assembled-output/README.txt", readme, P%
================================================
FILE: 1-source-files/main-sources/elite-source.asm
================================================
\ ******************************************************************************
\
\ BBC MICRO CASSETTE ELITE MAIN GAME SOURCE
\
\ BBC Micro cassette Elite was written by Ian Bell and David Braben and is
\ copyright Acornsoft 1984
\
\ The code in this file is identical to the source discs released on Ian Bell's
\ personal website at http://www.elitehomepage.org/ (it's just been reformatted
\ to be more readable)
\
\ 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://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file contains the main game code for BBC Micro cassette Elite. It
\ also contains the ship blueprints and game text.
\
\ ------------------------------------------------------------------------------
\
\ This source file produces the following binary files:
\
\ * ELTA.bin
\ * ELTB.bin
\ * ELTC.bin
\ * ELTD.bin
\ * ELTE.bin
\ * ELTF.bin
\ * ELTG.bin
\ * PYTHON.bin
\ * SHIPS.bin
\ * WORDS9.bin
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_SOURCE_DISC = (_VARIANT = 1)
_TEXT_SOURCES = (_VARIANT = 2)
_STH_CASSETTE = (_VARIANT = 3)
GUARD &6000 \ Guard against assembling over screen memory
\ ******************************************************************************
\
\ Configuration variables
\
\ ******************************************************************************
CODE% = &0F40 \ The address where the code will be run
LOAD% = &1128 \ The address where the code will be loaded
CODE_WORDS% = &0400 \ The address where the text data will be run
LOAD_WORDS% = &1100 \ The address where the text data will be loaded
Q% = _MAX_COMMANDER \ Set Q% to TRUE to max out the default commander, FALSE
\ for the standard default commander
NOST = 18 \ The number of stardust particles in normal space (this
\ goes down to 3 in witchspace)
NOSH = 12 \ The maximum number of ships in our local bubble of
\ universe
NTY = 13 \ The number of different ship types
COPS = 2 \ Ship type for a Viper
THG = 6 \ Ship type for a Thargoid
CYL = 7 \ Ship type for a Cobra Mk III (trader)
SST = 8 \ Ship type for the space station
MSL = 9 \ Ship type for a missile
AST = 10 \ Ship type for an asteroid
OIL = 11 \ Ship type for a cargo canister
TGL = 12 \ Ship type for a Thargon
ESC = 13 \ Ship type for an escape pod
POW = 15 \ Pulse laser power
NI% = 36 \ The number of bytes in each ship's data block (as
\ stored in INWK and K%)
VSCAN = 57 \ Defines the split position in the split-screen mode
X = 128 \ The centre x-coordinate of the 256 x 192 space view
Y = 96 \ The centre y-coordinate of the 256 x 192 space view
f0 = &20 \ Internal key number for red key f0 (Launch, Front)
f1 = &71 \ Internal key number for red key f1 (Buy Cargo, Rear)
f2 = &72 \ Internal key number for red key f2 (Sell Cargo, Left)
f3 = &73 \ Internal key number for red key f3 (Equip Ship, Right)
f4 = &14 \ Internal key number for red key f4 (Long-range Chart)
f5 = &74 \ Internal key number for red key f5 (Short-range Chart)
f6 = &75 \ Internal key number for red key f6 (Data on System)
f7 = &16 \ Internal key number for red key f7 (Market Price)
f8 = &76 \ Internal key number for red key f8 (Status Mode)
f9 = &77 \ Internal key number for red key f9 (Inventory)
RE = &23 \ The obfuscation byte used to hide the recursive tokens
\ table from crackers viewing the binary code
VIA = &FE00 \ Memory-mapped space for accessing internal hardware,
\ such as the video ULA, 6845 CRTC and 6522 VIAs (also
\ known as SHEILA)
OSBYTE = &FFF4 \ The address for the OSBYTE routine, which is used
\ three times in the main game code
OSWORD = &FFF1 \ The address for the OSWORD routine, which is used
\ twice in the main game code
OSFILE = &FFDD \ The address for the OSFILE routine, which is used
\ once in the main game code
\ ******************************************************************************
\
\ Name: ZP
\ Type: Workspace
\ Address: &0000 to &00E1
\ Category: Workspaces
\ Summary: Lots of important variables are stored in the zero page workspace
\ as it is quicker and more space-efficient to access memory here
\
\ ******************************************************************************
ORG &0000 \ Set the assembly address to &0000
.ZP
SKIP 0 \ The start of the zero page workspace
.RAND
SKIP 4 \ Four 8-bit seeds for the random number generation
\ system implemented in the DORND routine
.TRTB%
SKIP 2 \ Contains the address of the keyboard translation
\ table, which is used to translate internal key
\ numbers to ASCII
.T1
SKIP 1 \ Temporary storage, used in a number of places
.SC
SKIP 1 \ Screen address (low byte)
\
\ Elite draws on-screen by poking bytes directly into
\ screen memory, and SC(1 0) is typically set to the
\ address of the character block containing the pixel
\ we want to draw
.SCH
SKIP 1 \ Screen address (high byte)
.XX16
SKIP 18 \ Temporary storage for a block of values, used in a
\ number of places
.P
SKIP 3 \ Temporary storage, used in a number of places
.XX0
SKIP 2 \ Temporary storage, used to store the address of a ship
\ blueprint. For example, it is used when we add a new
\ ship to the local bubble in routine NWSHP, and it
\ contains the address of the current ship's blueprint
\ as we loop through all the nearby ships in the main
\ flight loop
.INF
SKIP 2 \ Temporary storage, typically used for storing the
\ address of a ship's data block, so it can be copied
\ to and from the internal workspace at INWK
.V
SKIP 2 \ Temporary storage, typically used for storing an
\ address pointer
.XX
SKIP 2 \ Temporary storage, typically used for storing a 16-bit
\ x-coordinate
.YY
SKIP 2 \ Temporary storage, typically used for storing a 16-bit
\ y-coordinate
.SUNX
SKIP 2 \ The 16-bit x-coordinate of the vertical centre axis
\ of the sun (which might be off-screen)
.BETA
SKIP 1 \ The current pitch angle beta, which is reduced from
\ JSTY to a sign-magnitude value between -8 and +8
\
\ This describes how fast we are pitching our ship, and
\ determines how fast the universe pitches around us
\
\ The sign bit is also stored in BET2, while the
\ opposite sign is stored in BET2+1
.BET1
SKIP 1 \ The magnitude of the pitch angle beta, i.e. |beta|,
\ which is a positive value between 0 and 8
.XC
SKIP 1 \ The x-coordinate of the text cursor (i.e. the text
\ column), which can be from 0 to 32
\
\ A value of 0 denotes the leftmost column and 32 the
\ rightmost column, but because the top part of the
\ screen (the space view) has a border box that
\ clashes with columns 0 and 32, text is only shown
\ in columns 1-31
.YC
SKIP 1 \ The y-coordinate of the text cursor (i.e. the text
\ row), which can be from 0 to 23
\
\ The screen actually has 31 character rows if you
\ include the dashboard, but the text printing routines
\ only work on the top part (the space view), so the
\ text cursor only goes up to a maximum of 23, the row
\ just before the screen splits
\
\ A value of 0 denotes the top row, but because the
\ top part of the screen has a border box that clashes
\ with row 0, text is always shown at row 1 or greater
.QQ22
SKIP 2 \ The two hyperspace countdown counters
\
\ Before a hyperspace jump, both QQ22 and QQ22+1 are
\ set to 15
\
\ QQ22 is an internal counter that counts down by 1
\ each time TT102 is called, which happens every
\ iteration of the main game loop. When it reaches
\ zero, the on-screen counter in QQ22+1 gets
\ decremented, and QQ22 gets set to 5 and the countdown
\ continues (so the first tick of the hyperspace counter
\ takes 15 iterations to happen, but subsequent ticks
\ take 5 iterations each)
\
\ QQ22+1 contains the number that's shown on-screen
\ during the countdown. It counts down from 15 to 1, and
\ when it hits 0, the hyperspace engines kick in
.ECMA
SKIP 1 \ The E.C.M. countdown timer, which determines whether
\ an E.C.M. system is currently operating
\
\ * 0 = E.C.M. is off
\
\ * Non-zero = E.C.M. is on and is counting down
\
\ The counter starts at 32 when an E.C.M. is activated,
\ either by us or by an opponent, and it decreases by 1
\ in each iteration of the main flight loop until it
\ reaches zero, at which point the E.C.M. switches off.
\ Only one E.C.M. can be active at any one time, so
\ there is only one counter
.XX15
SKIP 0 \ Temporary storage, typically used for storing screen
\ coordinates in line-drawing routines
\
\ There are six bytes of storage, from XX15 TO XX15+5.
\ The first four bytes have the following aliases:
\
\ X1 = XX15
\ Y1 = XX15+1
\ X2 = XX15+2
\ Y2 = XX15+3
\
\ These are typically used for describing lines in terms
\ of screen coordinates, i.e. (X1, Y1) to (X2, Y2)
\
\ The last two bytes of XX15 do not have aliases
.X1
SKIP 1 \ Temporary storage, typically used for x-coordinates in
\ the line-drawing routines
.Y1
SKIP 1 \ Temporary storage, typically used for y-coordinates in
\ line-drawing routines
.X2
SKIP 1 \ Temporary storage, typically used for x-coordinates in
\ the line-drawing routines
.Y2
SKIP 1 \ Temporary storage, typically used for y-coordinates in
\ line-drawing routines
SKIP 2 \ The last two bytes of the XX15 block
.XX12
SKIP 6 \ Temporary storage for a block of values, used in a
\ number of places
.K
SKIP 4 \ Temporary storage, used in a number of places
.KL
SKIP 1 \ The following bytes implement a key logger that
\ enables Elite to scan for concurrent key presses of
\ the primary flight keys, plus a secondary flight key
\
\ If a key is being pressed that is not in the keyboard
\ table at KYTB, it can be stored here (as seen in
\ routine DK4, for example)
.KY1
SKIP 1 \ "?" is being pressed (slow down)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY2
SKIP 1 \ Space is being pressed (speed up)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY3
SKIP 1 \ "<" is being pressed (roll left)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY4
SKIP 1 \ ">" is being pressed (roll right)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY5
SKIP 1 \ "X" is being pressed (pull up)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY6
SKIP 1 \ "S" is being pressed (pitch down)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY7
SKIP 1 \ "A" is being pressed (fire lasers)
\
\ * 0 = no
\
\ * Non-zero = yes
\
\ This is also set when the joystick fire button has
\ been pressed
.KY12
SKIP 1 \ TAB is being pressed (energy bomb)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY13
SKIP 1 \ ESCAPE is being pressed (launch escape pod)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY14
SKIP 1 \ "T" is being pressed (target missile)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY15
SKIP 1 \ "U" is being pressed (unarm missile)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY16
SKIP 1 \ "M" is being pressed (fire missile)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY17
SKIP 1 \ "E" is being pressed (activate E.C.M.)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY18
SKIP 1 \ "J" is being pressed (in-system jump)
\
\ * 0 = no
\
\ * Non-zero = yes
.KY19
SKIP 1 \ "C" is being pressed (activate docking computer)
\
\ * 0 = no
\
\ * Non-zero = yes
.LAS
SKIP 1 \ Contains the laser power of the laser fitted to the
\ current space view (or 0 if there is no laser fitted
\ to the current view)
\
\ This gets set to bits 0-6 of the laser power byte from
\ the commander data block, which contains the laser's
\ power (bit 7 doesn't denote laser power, just whether
\ or not the laser pulses, so that is not stored here)
.MSTG
SKIP 1 \ The current missile lock target
\
\ * &FF = no target
\
\ * 1-12 = the slot number of the ship that our
\ missile is locked onto
.XX1
SKIP 0 \ This is an alias for INWK that is used in the main
\ ship-drawing routine at LL9
.INWK
SKIP 33 \ The zero-page internal workspace for the current ship
\ data block
\
\ As operations on zero page locations are faster and
\ have smaller opcodes than operations on the rest of
\ the addressable memory, Elite tends to store oft-used
\ data here. A lot of the routines in Elite need to
\ access and manipulate ship data, so to make this an
\ efficient exercise, the ship data is first copied from
\ the ship data blocks at K% into INWK (or, when new
\ ships are spawned, from the blueprints at XX21)
.XX19
SKIP NI% - 33 \ XX19(1 0) shares its location with INWK(34 33), which
\ contains the address of the ship line heap
.LSP
SKIP 1 \ The ball line heap pointer, which contains the number
\ of the first free byte after the end of the LSX2 and
\ LSY2 heaps
.QQ15
SKIP 6 \ The three 16-bit seeds for the selected system, i.e.
\ the one in the crosshairs in the Short-range Chart
.K5
SKIP 0 \ Temporary storage used to store segment coordinates
\ across successive calls to BLINE, the ball line
\ routine
.XX18
SKIP 0 \ Temporary storage used to store coordinates in the
\ LL9 ship-drawing routine
.QQ17
SKIP 1 \ Contains a number of flags that affect how text tokens
\ are printed, particularly capitalisation
\
\ * If all bits are set (255) then text printing is
\ disabled
\
\ * Bit 7: 0 = ALL CAPS
\ 1 = Sentence Case, bit 6 determines the
\ case of the next letter to print
\
\ * Bit 6: 0 = print the next letter in upper case
\ 1 = print the next letter in lower case
\
\ * Bits 0-5: If any of bits 0-5 are set, print in
\ lower case
\
\ So:
\
\ * QQ17 = 0 means case is set to ALL CAPS
\
\ * QQ17 = %10000000 means Sentence Case, currently
\ printing upper case
\
\ * QQ17 = %11000000 means Sentence Case, currently
\ printing lower case
\
\ * QQ17 = %11111111 means printing is disabled
.QQ19
SKIP 3 \ Temporary storage, used in a number of places
.K6
SKIP 5 \ Temporary storage, typically used for storing
\ coordinates during vector calculations
.ALP1
SKIP 1 \ Magnitude of the roll angle alpha, i.e. |alpha|,
\ which is a positive value between 0 and 31
.ALP2
SKIP 2 \ Bit 7 of ALP2 = sign of the roll angle in ALPHA
\
\ Bit 7 of ALP2+1 = opposite sign to ALP2 and ALPHA
.BET2
SKIP 2 \ Bit 7 of BET2 = sign of the pitch angle in BETA
\
\ Bit 7 of BET2+1 = opposite sign to BET2 and BETA
.DELTA
SKIP 1 \ Our current speed, in the range 1-40
.DELT4
SKIP 2 \ Our current speed * 64 as a 16-bit value
\
\ This is stored as DELT4(1 0), so the high byte in
\ DELT4+1 therefore contains our current speed / 4
.U
SKIP 1 \ Temporary storage, used in a number of places
.Q
SKIP 1 \ Temporary storage, used in a number of places
.R
SKIP 1 \ Temporary storage, used in a number of places
.S
SKIP 1 \ Temporary storage, used in a number of places
.XSAV
SKIP 1 \ Temporary storage for saving the value of the X
\ register, used in a number of places
.YSAV
SKIP 1 \ Temporary storage for saving the value of the Y
\ register, used in a number of places
.XX17
SKIP 1 \ Temporary storage, used in BPRNT to store the number
\ of characters to print, and as the edge counter in the
\ main ship-drawing routine
.QQ11
SKIP 1 \ The type of the current view:
\
\ 0 = Space view
\ 1 = Data on System screen (red key f6)
\ Get commander name ("@", save/load commander)
\ In-system jump just arrived ("J")
\ Title screen
\ Buy Cargo screen (red key f1)
\ Mis-jump just arrived (witchspace)
\ 4 = Sell Cargo screen (red key f2)
\ 6 = Death screen
\ 8 = Status Mode screen (red key f8)
\ Inventory screen (red key f9)
\ 16 = Market Price screen (red key f7)
\ 32 = Equip Ship screen (red key f3)
\ 64 = Long-range Chart (red key f4)
\ 128 = Short-range Chart (red key f5)
\
\ This value is typically set by calling routine TT66
.ZZ
SKIP 1 \ Temporary storage, typically used for distance values
.XX13
SKIP 1 \ Temporary storage, typically used in the line-drawing
\ routines
.MCNT
SKIP 1 \ The main loop counter
\
\ This counter determines how often certain actions are
\ performed within the main loop
.DL
SKIP 1 \ Vertical sync flag
\
\ DL gets set to 30 every time we reach vertical sync on
\ the video system, which happens 50 times a second
\ (50Hz). The WSCAN routine uses this to pause until the
\ vertical sync, by setting DL to 0 and then monitoring
\ its value until it changes to 30
.TYPE
SKIP 1 \ The current ship type
\
\ This is where we store the current ship type for when
\ we are iterating through the ships in the local bubble
\ as part of the main flight loop. See the table at XX21
\ for information about ship types
.JSTX
SKIP 1 \ Our current roll rate
\
\ This value is shown in the dashboard's RL indicator,
\ and determines the rate at which we are rolling
\
\ The value ranges from 1 to 255 with 128 as the centre
\ point, so 1 means roll is decreasing at the maximum
\ rate, 128 means roll is not changing, and 255 means
\ roll is increasing at the maximum rate
\
\ This value is updated by "<" and ">" key presses, or
\ if joysticks are enabled, from the joystick. If
\ keyboard damping is enabled (which it is by default),
\ the value is slowly moved towards the centre value of
\ 128 (no roll) if there are no key presses or joystick
\ movement
.JSTY
SKIP 1 \ Our current pitch rate
\
\ This value is shown in the dashboard's DC indicator,
\ and determines the rate at which we are pitching
\
\ The value ranges from 1 to 255 with 128 as the centre
\ point, so 1 means pitch is decreasing at the maximum
\ rate, 128 means pitch is not changing, and 255 means
\ pitch is increasing at the maximum rate
\
\ This value is updated by "S" and "X" key presses, or
\ if joysticks are enabled, from the joystick. If
\ keyboard damping is enabled (which it is by default),
\ the value is slowly moved towards the centre value of
\ 128 (no pitch) if there are no key presses or joystick
\ movement
.ALPHA
SKIP 1 \ The current roll angle alpha, which is reduced from
\ JSTX to a sign-magnitude value between -31 and +31
\
\ This describes how fast we are rolling our ship, and
\ determines how fast the universe rolls around us
\
\ The sign bit is also stored in ALP2, while the
\ opposite sign is stored in ALP2+1
.QQ12
SKIP 1 \ Our "docked" status
\
\ * 0 = we are not docked
\
\ * &FF = we are docked
.TGT
SKIP 1 \ Temporary storage, typically used as a target value
\ for counters when drawing explosion clouds and partial
\ circles
.SWAP
SKIP 1 \ Temporary storage, used to store a flag that records
\ whether or not we had to swap a line's start and end
\ coordinates around when clipping the line in routine
\ LL145 (the flag is used in places like BLINE to swap
\ them back)
.COL
SKIP 1 \ Temporary storage, used to store colour information
\ when drawing pixels in the dashboard
.FLAG
SKIP 1 \ A flag that's used to define whether this is the first
\ call to the ball line routine in BLINE, so it knows
\ whether to wait for the second call before storing
\ segment data in the ball line heap
.CNT
SKIP 1 \ Temporary storage, typically used for storing the
\ number of iterations required when looping
.CNT2
SKIP 1 \ Temporary storage, used in the planet-drawing routine
\ to store the segment number where the arc of a partial
\ circle should start
.STP
SKIP 1 \ The step size for drawing circles
\
\ Circles in Elite are split up into 64 points, and the
\ step size determines how many points to skip with each
\ straight-line segment, so the smaller the step size,
\ the smoother the circle. The values used are:
\
\ * 2 for big planets and the circles on the charts
\
\ * 4 for medium planets and the launch tunnel
\
\ * 8 for small planets and the hyperspace tunnel
\
\ As the step size increases we move from smoother
\ circles at the top to more polygonal at the bottom.
\ See the CIRCLE2 routine for more details
.XX4
SKIP 1 \ Temporary storage, used in a number of places
.XX20
SKIP 1 \ Temporary storage, used in a number of places
.XX14
SKIP 1 \ This byte appears to be unused
.RAT
SKIP 1 \ Used to store different signs depending on the current
\ space view, for use in calculating stardust movement
.RAT2
SKIP 1 \ Temporary storage, used to store the pitch and roll
\ signs when moving objects and stardust
.K2
SKIP 4 \ Temporary storage, used in a number of places
ORG &00D1 \ Set the assembly address to &00D1
.T
SKIP 1 \ Temporary storage, used in a number of places
.K3
SKIP 0 \ Temporary storage, used in a number of places
.XX2
SKIP 14 \ Temporary storage, used to store the visibility of the
\ ship's faces during the ship-drawing routine at LL9
.K4
SKIP 2 \ Temporary storage, used in a number of places
PRINT "ZP workspace from ", ~ZP, "to ", ~P%-1, "inclusive"
\ ******************************************************************************
\
\ Name: XX3
\ Type: Workspace
\ Address: &0100 to the top of the descending stack
\ Category: Workspaces
\ Summary: Temporary storage space for complex calculations
\
\ ------------------------------------------------------------------------------
\
\ Used as heap space for storing temporary data during calculations. Shared with
\ the descending 6502 stack, which works down from &01FF.
\
\ ******************************************************************************
ORG &0100 \ Set the assembly address to &0100
.XX3
SKIP 256 \ Temporary storage, typically used for storing tables
\ of values such as screen coordinates or ship data
\ ******************************************************************************
\
\ Name: T%
\ Type: Workspace
\ Address: &0300 to &0371
\ Category: Workspaces
\ Summary: Current commander data and stardust data blocks
\
\ ------------------------------------------------------------------------------
\
\ Contains the current commander data (NT% bytes at location TP), and the
\ stardust data blocks (NOST bytes at location SX)
\
\ ******************************************************************************
ORG &0300 \ Set the assembly address to &0300
.T%
SKIP 0 \ The start of the T% workspace
.TP
SKIP 1 \ The current mission status, which is always 0 for the
\ cassette version of Elite as there are no missions
.QQ0
SKIP 1 \ The current system's galactic x-coordinate (0-256)
.QQ1
SKIP 1 \ The current system's galactic y-coordinate (0-256)
.QQ21
SKIP 6 \ The three 16-bit seeds for the current galaxy
\
\ These seeds define system 0 in the current galaxy, so
\ they can be used as a starting point to generate all
\ 256 systems in the galaxy
\
\ Using a galactic hyperdrive rotates each byte to the
\ left (rolling each byte within itself) to get the
\ seeds for the next galaxy, so after eight galactic
\ jumps, the seeds roll around to the first galaxy again
.CASH
SKIP 4 \ Our current cash pot
\
\ The cash stash is stored as a 32-bit unsigned integer,
\ with the most significant byte in CASH and the least
\ significant in CASH+3. This is big-endian, which is
\ the opposite way round to most of the numbers used in
\ Elite - to use our notation for multi-byte numbers,
\ the amount of cash is CASH(0 1 2 3)
.QQ14
SKIP 1 \ Our current fuel level (0-70)
\
\ The fuel level is stored as the number of light years
\ multiplied by 10, so QQ14 = 1 represents 0.1 light
\ years, and the maximum possible value is 70, for 7.0
\ light years
.COK
SKIP 1 \ Flags used to generate the competition code
.GCNT
SKIP 1 \ The number of the current galaxy (0-7)
\
\ When this is displayed in-game, 1 is added to the
\ number, so we start in galaxy 1 in-game, but it's
\ stored as galaxy 0 internally
\
\ The galaxy number increases by one every time a
\ galactic hyperdrive is used, and wraps back around to
\ the start after eight galaxies
.LASER
SKIP 4 \ The specifications of the lasers fitted to each of the
\ four space views
\
\ * Byte #0 = front view
\
\ * Byte #1 = rear view
\
\ * Byte #2 = left view
\
\ * Byte #3 = right view
\
\ The value for each view is as follows:
\
\ * 0 = no laser is fitted to this view
\
\ * Non-zero = a laser is fitted to this view, with
\ the following specification:
\
\ * Bits 0-6 contain the laser's power
\
\ * Bit 7 determines whether or not the laser pulses
\ (0 = pulse laser) or is always on (1 = beam
\ laser)
SKIP 2 \ These bytes appear to be unused (they were originally
\ used for up/down lasers, but they were dropped)
.CRGO
SKIP 1 \ Our ship's cargo capacity
\
\ * 22 = standard cargo bay of 20 tonnes
\
\ * 37 = large cargo bay of 35 tonnes
\
\ The value is two greater than the actual capacity to
\ make the maths in tnpr slightly more efficient
.QQ20
SKIP 17 \ The contents of our cargo hold
\
\ The amount of market item X that we have in our hold
\ can be found in the X-th byte of QQ20. For example:
\
\ * QQ20 contains the amount of food (item 0)
\
\ * QQ20+7 contains the amount of computers (item 7)
\
\ See QQ23 for a list of market item numbers and their
\ storage units
.ECM
SKIP 1 \ E.C.M. system
\
\ * 0 = not fitted
\
\ * &FF = fitted
.BST
SKIP 1 \ Fuel scoops (BST stands for "barrel status")
\
\ * 0 = not fitted
\
\ * &FF = fitted
.BOMB
SKIP 1 \ Energy bomb
\
\ * 0 = not fitted
\
\ * &7F = fitted
.ENGY
SKIP 1 \ Energy unit
\
\ * 0 = not fitted
\
\ * Non-zero = fitted
\
\ The actual value determines the refresh rate of our
\ energy banks, as they refresh by ENGY+1 each time (so
\ our ship's energy level goes up by 2 each time if we
\ have an energy unit fitted, otherwise it goes up by 1)
.DKCMP
SKIP 1 \ Docking computer
\
\ * 0 = not fitted
\
\ * &FF = fitted
.GHYP
SKIP 1 \ Galactic hyperdrive
\
\ * 0 = not fitted
\
\ * &FF = fitted
.ESCP
SKIP 1 \ Escape pod
\
\ * 0 = not fitted
\
\ * &FF = fitted
SKIP 4 \ These bytes appear to be unused
.NOMSL
SKIP 1 \ The number of missiles we have fitted (0-4)
.FIST
SKIP 1 \ Our legal status (FIST stands for "fugitive/innocent
\ status")
\
\ * 0 = Clean
\
\ * 1-49 = Offender
\
\ * 50+ = Fugitive
\
\ You get 64 points if you kill a cop, so that's a fast
\ ticket to fugitive status
.AVL
SKIP 17 \ Market availability in the current system
\
\ The available amount of market item X is stored in
\ the X-th byte of AVL, so for example:
\
\ * AVL contains the amount of food (item 0)
\
\ * AVL+7 contains the amount of computers (item 7)
\
\ See QQ23 for a list of market item numbers and their
\ storage units
.QQ26
SKIP 1 \ A random value used to randomise market data
\
\ This value is set to a new random number for each
\ change of system, so we can add a random factor into
\ the calculations for market prices
.TALLY
SKIP 2 \ Our combat rank
\
\ The combat rank is stored as the number of kills, in a
\ 16-bit number TALLY(1 0) - so the high byte is in
\ TALLY+1 and the low byte in TALLY
\
\ If the high byte in TALLY+1 is 0 then we have between
\ 0 and 255 kills, so our rank is Harmless, Mostly
\ Harmless, Poor, Average Above Average or Competent,
\ according to the value of the low byte in TALLY:
\
\ Harmless %00000000 to %00000111 = 0 to 7
\ Mostly Harmless %00001000 to %00001111 = 8 to 15
\ Poor %00010000 to %00011111 = 16 to 31
\ Average %00100000 to %00111111 = 32 to 63
\ Above Average %01000000 to %01111111 = 64 to 127
\ Competent %10000000 to %11111111 = 128 to 255
\
\ Note that the Competent range also covers kill counts
\ from 256 to 511, as follows
\
\ If the high byte in TALLY+1 is non-zero then we are
\ Competent, Dangerous, Deadly or Elite, according to
\ the value of TALLY(1 0):
\
\ Competent (1 0) to (1 255) = 256 to 511 kills
\ Dangerous (2 0) to (9 255) = 512 to 2559 kills
\ Deadly (10 0) to (24 255) = 2560 to 6399 kills
\ Elite (25 0) and up = 6400 kills and up
\
\ You can see the rating calculation in the STATUS
\ subroutine
.SVC
SKIP 1 \ The save count
\
\ When a new commander is created, the save count gets
\ set to 128. This value gets halved each time the
\ commander file is saved, but it is otherwise unused.
\ It is presumably part of the security system for the
\ competition, possibly another flag to catch out
\ entries with manually altered commander files
SKIP 2 \ The commander file checksum
\
\ These two bytes are reserved for the commander file
\ checksum, so when the current commander block is
\ copied from here to the last saved commander block at
\ NA%, CHK and CHK2 get overwritten
NT% = SVC + 2 - TP \ This sets the variable NT% to the size of the current
\ commander data block, which starts at TP and ends at
\ SVC+2 (inclusive)
.SX
SKIP NOST + 1 \ This is where we store the x_hi coordinates for all
\ the stardust particles
.SXL
SKIP NOST + 1 \ This is where we store the x_lo coordinates for all
\ the stardust particles
PRINT "T% workspace from ", ~T%, "to ", ~P%-1, "inclusive"
\ ******************************************************************************
\
\ ELITE RECURSIVE TEXT TOKEN FILE
\
\ Produces the binary file WORDS9.bin that gets loaded by elite-loader.asm.
\
\ The recursive token table is loaded at &1100 and is moved down to &0400 as
\ part of elite-loader.asm, so it ends up at &0400 to &07FF.
\
\ ******************************************************************************
ORG CODE_WORDS% \ Set the assembly address to CODE_WORDS%
\ ******************************************************************************
\
\ Name: CHAR
\ Type: Macro
\ Category: Text
\ Summary: Macro definition for characters in the recursive token table
\ Deep dive: Printing text tokens
\
\ ------------------------------------------------------------------------------
\
\ The following macro is used when building the recursive token table:
\
\ CHAR 'x' Insert ASCII character "x"
\
\ To include an apostrophe, use a backtick character, as in CHAR '`'.
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ 'x' The character to insert into the table
\
\ ******************************************************************************
MACRO CHAR x
IF x = '`'
EQUB 39 EOR RE
ELSE
EQUB x EOR RE
ENDIF
ENDMACRO
\ ******************************************************************************
\
\ Name: TWOK
\ Type: Macro
\ Category: Text
\ Summary: Macro definition for two-letter tokens in the token table
\ Deep dive: Printing text tokens
\
\ ------------------------------------------------------------------------------
\
\ The following macro is used when building the recursive token table:
\
\ TWOK 'x', 'y' Insert two-letter token "xy"
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ 'x' The first letter of the two-letter token to insert into
\ the table
\
\ 'y' The second letter of the two-letter token to insert into
\ the table
\
\ ******************************************************************************
MACRO TWOK t, k
IF t = 'A' AND k = 'L'
EQUB 128 EOR RE
ENDIF
IF t = 'L' AND k = 'E'
EQUB 129 EOR RE
ENDIF
IF t = 'X' AND k = 'E'
EQUB 130 EOR RE
ENDIF
IF t = 'G' AND k = 'E'
EQUB 131 EOR RE
ENDIF
IF t = 'Z' AND k = 'A'
EQUB 132 EOR RE
ENDIF
IF t = 'C' AND k = 'E'
EQUB 133 EOR RE
ENDIF
IF t = 'B' AND k = 'I'
EQUB 134 EOR RE
ENDIF
IF t = 'S' AND k = 'O'
EQUB 135 EOR RE
ENDIF
IF t = 'U' AND k = 'S'
EQUB 136 EOR RE
ENDIF
IF t = 'E' AND k = 'S'
EQUB 137 EOR RE
ENDIF
IF t = 'A' AND k = 'R'
EQUB 138 EOR RE
ENDIF
IF t = 'M' AND k = 'A'
EQUB 139 EOR RE
ENDIF
IF t = 'I' AND k = 'N'
EQUB 140 EOR RE
ENDIF
IF t = 'D' AND k = 'I'
EQUB 141 EOR RE
ENDIF
IF t = 'R' AND k = 'E'
EQUB 142 EOR RE
ENDIF
IF t = 'A' AND k = '?'
EQUB 143 EOR RE
ENDIF
IF t = 'E' AND k = 'R'
EQUB 144 EOR RE
ENDIF
IF t = 'A' AND k = 'T'
EQUB 145 EOR RE
ENDIF
IF t = 'E' AND k = 'N'
EQUB 146 EOR RE
ENDIF
IF t = 'B' AND k = 'E'
EQUB 147 EOR RE
ENDIF
IF t = 'R' AND k = 'A'
EQUB 148 EOR RE
ENDIF
IF t = 'L' AND k = 'A'
EQUB 149 EOR RE
ENDIF
IF t = 'V' AND k = 'E'
EQUB 150 EOR RE
ENDIF
IF t = 'T' AND k = 'I'
EQUB 151 EOR RE
ENDIF
IF t = 'E' AND k = 'D'
EQUB 152 EOR RE
ENDIF
IF t = 'O' AND k = 'R'
EQUB 153 EOR RE
ENDIF
IF t = 'Q' AND k = 'U'
EQUB 154 EOR RE
ENDIF
IF t = 'A' AND k = 'N'
EQUB 155 EOR RE
ENDIF
IF t = 'T' AND k = 'E'
EQUB 156 EOR RE
ENDIF
IF t = 'I' AND k = 'S'
EQUB 157 EOR RE
ENDIF
IF t = 'R' AND k = 'I'
EQUB 158 EOR RE
ENDIF
IF t = 'O' AND k = 'N'
EQUB 159 EOR RE
ENDIF
ENDMACRO
\ ******************************************************************************
\
\ Name: CONT
\ Type: Macro
\ Category: Text
\ Summary: Macro definition for control codes in the recursive token table
\ Deep dive: Printing text tokens
\
\ ------------------------------------------------------------------------------
\
\ The following macro is used when building the recursive token table:
\
\ CONT n Insert control code token {n}
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ n The control code to insert into the table
\
\ ******************************************************************************
MACRO CONT n
EQUB n EOR RE
ENDMACRO
\ ******************************************************************************
\
\ Name: RTOK
\ Type: Macro
\ Category: Text
\ Summary: Macro definition for recursive tokens in the recursive token table
\ Deep dive: Printing text tokens
\
\ ------------------------------------------------------------------------------
\
\ The following macro is used when building the recursive token table:
\
\ RTOK n Insert recursive token [n]
\
\ * Tokens 0-95 get stored as n + 160
\
\ * Tokens 128-145 get stored as n - 114
\
\ * Tokens 96-127 get stored as n
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ n The number of the recursive token to insert into the
\ table, in the range 0 to 145
\
\ ******************************************************************************
MACRO RTOK n
IF n >= 0 AND n <= 95
t = n + 160
ELIF n >= 128
t = n - 114
ELSE
t = n
ENDIF
EQUB t EOR RE
ENDMACRO
\ ******************************************************************************
\
\ Name: QQ18
\ Type: Variable
\ Category: Text
\ Summary: The recursive token table for tokens 0-148
\ Deep dive: Printing text tokens
\
\ ------------------------------------------------------------------------------
\
\ The encodings shown for each recursive text token use the following notation:
\
\ {n} Control code n = 0 to 13
\ <n> Two-letter token n = 128 to 159
\ [n] Recursive token n = 0 to 148
\
\ ******************************************************************************
.QQ18
RTOK 111 \ Token 0: "FUEL SCOOPS ON {beep}"
RTOK 131 \
CONT 7 \ Encoded as: "[111][131]{7}"
EQUB 0
CHAR ' ' \ Token 1: " CHART"
CHAR 'C' \
CHAR 'H' \ Encoded as: " CH<138>T"
TWOK 'A', 'R'
CHAR 'T'
EQUB 0
CHAR 'G' \ Token 2: "GOVERNMENT"
CHAR 'O' \
TWOK 'V', 'E' \ Encoded as: "GO<150>RNM<146>T"
CHAR 'R'
CHAR 'N'
CHAR 'M'
TWOK 'E', 'N'
CHAR 'T'
EQUB 0
CHAR 'D' \ Token 3: "DATA ON {selected system name}"
TWOK 'A', 'T' \
CHAR 'A' \ Encoded as: "D<145>A[131]{3}"
RTOK 131
CONT 3
EQUB 0
TWOK 'I', 'N' \ Token 4: "INVENTORY{crlf}
TWOK 'V', 'E' \ "
CHAR 'N' \
CHAR 'T' \ Encoded as: "<140><150>NT<153>Y{13}"
TWOK 'O', 'R'
CHAR 'Y'
CONT 13
EQUB 0
CHAR 'S' \ Token 5: "SYSTEM"
CHAR 'Y' \
CHAR 'S' \ Encoded as: "SYS<156>M"
TWOK 'T', 'E'
CHAR 'M'
EQUB 0
CHAR 'P' \ Token 6: "PRICE"
TWOK 'R', 'I' \
TWOK 'C', 'E' \ Encoded as: "P<158><133>"
EQUB 0
CONT 2 \ Token 7: "{current system name} MARKET PRICES"
CHAR ' ' \
TWOK 'M', 'A' \ Encoded as: "{2} <139>RKET [6]S"
CHAR 'R'
CHAR 'K'
CHAR 'E'
CHAR 'T'
CHAR ' '
RTOK 6
CHAR 'S'
EQUB 0
TWOK 'I', 'N' \ Token 8: "INDUSTRIAL"
CHAR 'D' \
TWOK 'U', 'S' \ Encoded as: "<140>D<136>T<158><128>"
CHAR 'T'
TWOK 'R', 'I'
TWOK 'A', 'L'
EQUB 0
CHAR 'A' \ Token 9: "AGRICULTURAL"
CHAR 'G' \
TWOK 'R', 'I' \ Encoded as: "AG<158>CULTU<148>L"
CHAR 'C'
CHAR 'U'
CHAR 'L'
CHAR 'T'
CHAR 'U'
TWOK 'R', 'A'
CHAR 'L'
EQUB 0
TWOK 'R', 'I' \ Token 10: "RICH "
CHAR 'C' \
CHAR 'H' \ Encoded as: "<158>CH "
CHAR ' '
EQUB 0
CHAR 'A' \ Token 11: "AVERAGE "
TWOK 'V', 'E' \
TWOK 'R', 'A' \ Encoded as: "A<150><148><131> "
TWOK 'G', 'E'
CHAR ' '
EQUB 0
CHAR 'P' \ Token 12: "POOR "
CHAR 'O' \
TWOK 'O', 'R' \ Encoded as: "PO<153> "
CHAR ' '
EQUB 0
TWOK 'M', 'A' \ Token 13: "MAINLY "
TWOK 'I', 'N' \
CHAR 'L' \ Encoded as: "<139><140>LY "
CHAR 'Y'
CHAR ' '
EQUB 0
CHAR 'U' \ Token 14: "UNIT"
CHAR 'N' \
CHAR 'I' \ Encoded as: "UNIT"
CHAR 'T'
EQUB 0
CHAR 'V' \ Token 15: "VIEW "
CHAR 'I' \
CHAR 'E' \ Encoded as: "VIEW "
CHAR 'W'
CHAR ' '
EQUB 0
TWOK 'Q', 'U' \ Token 16: "QUANTITY"
TWOK 'A', 'N' \
TWOK 'T', 'I' \ Encoded as: "<154><155><151>TY"
CHAR 'T'
CHAR 'Y'
EQUB 0
TWOK 'A', 'N' \ Token 17: "ANARCHY"
TWOK 'A', 'R' \
CHAR 'C' \ Encoded as: "<155><138>CHY"
CHAR 'H'
CHAR 'Y'
EQUB 0
CHAR 'F' \ Token 18: "FEUDAL"
CHAR 'E' \
CHAR 'U' \ Encoded as: "FEUD<128>"
CHAR 'D'
TWOK 'A', 'L'
EQUB 0
CHAR 'M' \ Token 19: "MULTI-GOVERNMENT"
CHAR 'U' \
CHAR 'L' \ Encoded as: "MUL<151>-[2]"
TWOK 'T', 'I'
CHAR '-'
RTOK 2
EQUB 0
TWOK 'D', 'I' \ Token 20: "DICTATORSHIP"
CHAR 'C' \
CHAR 'T' \ Encoded as: "<141>CT<145><153>[25]"
TWOK 'A', 'T'
TWOK 'O', 'R'
RTOK 25
EQUB 0
RTOK 91 \ Token 21: "COMMUNIST"
CHAR 'M' \
CHAR 'U' \ Encoded as: "[91]MUN<157>T"
CHAR 'N'
TWOK 'I', 'S'
CHAR 'T'
EQUB 0
CHAR 'C' \ Token 22: "CONFEDERACY"
TWOK 'O', 'N' \
CHAR 'F' \ Encoded as: "C<159>F<152><144>ACY"
TWOK 'E', 'D'
TWOK 'E', 'R'
CHAR 'A'
CHAR 'C'
CHAR 'Y'
EQUB 0
CHAR 'D' \ Token 23: "DEMOCRACY"
CHAR 'E' \
CHAR 'M' \ Encoded as: "DEMOC<148>CY"
CHAR 'O'
CHAR 'C'
TWOK 'R', 'A'
CHAR 'C'
CHAR 'Y'
EQUB 0
CHAR 'C' \ Token 24: "CORPORATE STATE"
TWOK 'O', 'R' \
CHAR 'P' \ Encoded as: "C<153>P<153><145>E [43]<145>E"
TWOK 'O', 'R'
TWOK 'A', 'T'
CHAR 'E'
CHAR ' '
RTOK 43
TWOK 'A', 'T'
CHAR 'E'
EQUB 0
CHAR 'S' \ Token 25: "SHIP"
CHAR 'H' \
CHAR 'I' \ Encoded as: "SHIP"
CHAR 'P'
EQUB 0
CHAR 'P' \ Token 26: "PRODUCT"
CHAR 'R' \
CHAR 'O' \ Encoded as: "PRODUCT"
CHAR 'D'
CHAR 'U'
CHAR 'C'
CHAR 'T'
EQUB 0
CHAR ' ' \ Token 27: " LASER"
TWOK 'L', 'A' \
CHAR 'S' \ Encoded as: " <149>S<144>"
TWOK 'E', 'R'
EQUB 0
CHAR 'H' \ Token 28: "HUMAN COLONIAL"
CHAR 'U' \
CHAR 'M' \ Encoded as: "HUM<155> COL<159>I<128>"
TWOK 'A', 'N'
CHAR ' '
CHAR 'C'
CHAR 'O'
CHAR 'L'
TWOK 'O', 'N'
CHAR 'I'
TWOK 'A', 'L'
EQUB 0
CHAR 'H' \ Token 29: "HYPERSPACE "
CHAR 'Y' \
CHAR 'P' \ Encoded as: "HYP<144>SPA<133> "
TWOK 'E', 'R'
CHAR 'S'
CHAR 'P'
CHAR 'A'
TWOK 'C', 'E'
CHAR ' '
EQUB 0
CHAR 'S' \ Token 30: "SHORT RANGE CHART"
CHAR 'H' \
TWOK 'O', 'R' \ Encoded as: "SH<153>T [42][1]"
CHAR 'T'
CHAR ' '
RTOK 42
RTOK 1
EQUB 0
TWOK 'D', 'I' \ Token 31: "DISTANCE"
RTOK 43 \
TWOK 'A', 'N' \ Encoded as: "<141>[43]<155><133>"
TWOK 'C', 'E'
EQUB 0
gitextract_xn5smrlz/ ├── .gitattributes ├── .gitignore ├── 1-source-files/ │ ├── README.md │ ├── basic-programs/ │ │ └── README.md │ ├── boot-files/ │ │ └── README.md │ ├── images/ │ │ └── README.md │ ├── main-sources/ │ │ ├── README.md │ │ ├── elite-bcfs.asm │ │ ├── elite-build-options.asm │ │ ├── elite-disc.asm │ │ ├── elite-loader.asm │ │ ├── elite-readme.asm │ │ └── elite-source.asm │ └── original-sources/ │ ├── $.DEFEDIT.inf │ ├── $.DEFEDIT.txt │ ├── $.DEFGEN.inf │ ├── $.DEFGEN.txt │ ├── $.DEFTRAN.inf │ ├── $.DEFTRAN.txt │ ├── $.DIALGEN.inf │ ├── $.DIALGEN.txt │ ├── $.ELITEA.inf │ ├── $.ELITEA.txt │ ├── $.ELITEB.inf │ ├── $.ELITEB.txt │ ├── $.ELITEC.inf │ ├── $.ELITEC.txt │ ├── $.ELITED.inf │ ├── $.ELITED.txt │ ├── $.ELITEE.inf │ ├── $.ELITEE.txt │ ├── $.ELITEF.inf │ ├── $.ELITEF.txt │ ├── $.ELITEG.inf │ ├── $.ELITEG.txt │ ├── $.ELITES.inf │ ├── $.ELITES.txt │ ├── $.FINDSC2.inf │ ├── $.FINDSC2.txt │ ├── $.GENTOK.inf │ ├── $.GENTOK.txt │ ├── $.GETBIT.inf │ ├── $.GETBIT.txt │ ├── $.MAKER.inf │ ├── $.MAKER.txt │ ├── $.SBLOCK.inf │ ├── $.SBLOCK.txt │ ├── $.SFTPROT.inf │ ├── $.SFTPROT.txt │ ├── $.SHPPRTE.inf │ ├── $.SHPPRTE.txt │ ├── $.TOKPRI.inf │ ├── $.TOKPRI.txt │ ├── $.UNPACK.inf │ ├── $.UNPACK.txt │ ├── A.GENTOK.inf │ ├── A.GENTOK.txt │ ├── O.ELITED.inf │ ├── O.ELITED.txt │ ├── README.md │ ├── S.BCFS.inf │ ├── S.BCFS.txt │ ├── S.GENTOK.inf │ ├── S.GENTOK.txt │ ├── S.STEST.inf │ ├── S.STEST.txt │ └── text-sources/ │ ├── ELITEA.txt │ ├── ELITEB.txt │ ├── ELITEC.txt │ ├── ELITED.txt │ ├── ELITEE.txt │ ├── ELITEF.txt │ └── ELITEG.txt ├── 2-build-files/ │ ├── README.md │ ├── crc32.py │ ├── elite-checksum.py │ ├── elite-decrypt.py │ ├── mktibet-0.3.php │ └── tibetuef-0.8.php ├── 3-assembled-output/ │ ├── README.md │ ├── README.txt │ └── compile.txt ├── 4-reference-binaries/ │ └── README.md ├── 5-compiled-game-discs/ │ ├── README.md │ ├── elite-cassette-from-source-disc.ssd │ ├── elite-cassette-from-text-sources.ssd │ ├── elite-cassette-sth.ssd │ └── elite-cassette-sth.uef ├── Makefile ├── README.md └── make.bat
SYMBOL INDEX (71 symbols across 3 files)
FILE: 2-build-files/crc32.py
function main (line 21) | def main():
FILE: 2-build-files/mktibet-0.3.php
class FileOpts (line 64) | class FileOpts {
method __construct (line 76) | function __construct() {
method to_string (line 87) | function to_string() : string {
class TibetFile (line 100) | class TibetFile {
method __construct (line 108) | function __construct (int $baud) {
method to_string (line 118) | function to_string() : string {
function process (line 129) | function process(array $argv) {
function build_tibet (line 168) | function build_tibet (array $files, string &$out) : int {
function tibet_encode (line 219) | function tibet_encode (string $in, int $baud, bool $baud_changed, string...
function build_block (line 248) | function build_block (int $bn,
function acorn_crc (line 274) | function acorn_crc (string $s) : int {
function to_be16 (line 300) | function to_be16 (int $i) : string {
function to_le16 (line 308) | function to_le16 (int $i) : string {
function to_le32 (line 316) | function to_le32 (int $i) : string {
function load_files (line 326) | function load_files (array &$files) : int {
function fill_missing_names (line 345) | function fill_missing_names (array &$a) : int {
function parse_cli (line 359) | function parse_cli (array $argv, array &$files, string &$tibet_fn) : int {
function check_mos_filename (line 475) | function check_mos_filename (string $v) : int {
function cli_parse_baud (line 494) | function cli_parse_baud (string $v, int &$baud_out) : int {
function cli_parse_x32 (line 507) | function cli_parse_x32 (string $v, int &$out) : int {
function usage (line 542) | function usage (string $argv0) {
FILE: 2-build-files/tibetuef-0.8.php
class Span (line 61) | class Span {
class TimeHint (line 66) | class TimeHint extends Span {
class ParsedTibet (line 70) | class ParsedTibet {
method __construct (line 73) | function __construct() {
class TibetSilence (line 79) | class TibetSilence extends Span {
class TibetLeader (line 83) | class TibetLeader extends Span {
class DataFraming (line 87) | class DataFraming extends Span {
method to_string (line 92) | function to_string() : string {
method __construct (line 95) | function __construct() {
class BaudRate (line 103) | class BaudRate extends Span {
method to_string (line 105) | function to_string() : string {
method __construct (line 108) | function __construct() {
class TibetData (line 114) | class TibetData extends Span {
method __construct (line 119) | function __construct() {
class TibetCycle (line 126) | class TibetCycle extends Span {
class DummyByte (line 130) | class DummyByte extends Span {
function tbt_main (line 142) | function tbt_main ($argc, $argv) : int {
function spans_fix_up_long_leader (line 340) | function spans_fix_up_long_leader (ParsedTibet &$tbt) : int {
function print_chunk_messages (line 366) | function print_chunk_messages ($msg) {
function cycles_to_bits (line 371) | function cycles_to_bits (ParsedTibet &$tbt) : int {
function populate_framings_from_tibet (line 502) | function populate_framings_from_tibet (ParsedTibet &$tbt) : int {
function spans_detect_and_convert_dummy_bits (line 726) | function spans_detect_and_convert_dummy_bits (ParsedTibet &$tbt) : int {
function build_uef (line 807) | function build_uef (ParsedTibet $tbt,
function build_dummy_byte (line 957) | function build_dummy_byte (DummyByte $db, string &$chunkbuf) : int {
function build_uef_baud (line 962) | function build_uef_baud (int $baud, string &$chunkbuf) : int {
function build_uef_leader (line 967) | function build_uef_leader (int $num_cycs, string &$chunkbuf) : int {
function build_uef_silence_116 (line 972) | function build_uef_silence_116 (TibetSilence $tbs, string &$chunkbuf) : ...
function build_uef_silence_112 (line 980) | function build_uef_silence_112 (TibetSilence $tbs, string &$uef, string ...
function build_uef_squawk (line 998) | function build_uef_squawk (TibetData $tbd,
function build_uef_data (line 1087) | function build_uef_data (TibetData $tbd,
function build_uef_data_114 (line 1161) | function build_uef_data_114 (TibetData $tbd,
function build_uef_data_100 (line 1211) | function build_uef_data_100 (TibetData $tbd,
function build_uef_data_104 (line 1274) | function build_uef_data_104 (TibetData $tbd,
function build_uef_data_102 (line 1425) | function build_uef_data_102 (TibetData $tbd,
function check_parity (line 1559) | function check_parity (int $word, int $parity_bit, string $parity_mode) ...
function frexp (line 1572) | function frexp ( $f, &$exponent) {
function uef_float (line 1577) | function uef_float (float $f) : string {
function wrap_chunk (line 1614) | function wrap_chunk (int $type, string $in, string &$msg) : string {
function le16 (line 1626) | function le16 (int $i) : string {
function le32 (line 1633) | function le32 (int $i) : string {
function le24 (line 1642) | function le24 (int $i) : string {
function process_line (line 1652) | function process_line (int $ln,
function parse_float (line 1851) | function parse_float (string $w, float &$f) : int {
function parse_int (line 1874) | function parse_int (string $w, int &$i) : int {
function parse_framing (line 1890) | function parse_framing (string $w, DataFraming &$f) : int {
function parse_baudrate (line 1907) | function parse_baudrate (string $w, BaudRate &$r) : int {
function parse_version (line 1916) | function parse_version (array $words, int $ln, string &$tbt_version, str...
function tbt_process (line 1942) | function tbt_process (string $ip, bool $insert_timestamps, ParsedTibet &...
function usage (line 1960) | function usage(string $argv0) {
Condensed preview — 91 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,491K chars).
[
{
"path": ".gitattributes",
"chars": 398,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs diff=csharp\n\n# St"
},
{
"path": ".gitignore",
"chars": 280,
"preview": "# Windows thumbnail cache files\nThumbs.db\nehthumbs.db\nehthumbs_vista.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin"
},
{
"path": "1-source-files/README.md",
"chars": 634,
"preview": "# Source files for the BBC Micro cassette version of Elite\n\nThis folder contains the source files for the BBC Micro cass"
},
{
"path": "1-source-files/basic-programs/README.md",
"chars": 672,
"preview": "# BASIC programs for the BBC Micro cassette version of Elite\n\nThis folder contains the BASIC programs from the original "
},
{
"path": "1-source-files/boot-files/README.md",
"chars": 311,
"preview": "# Boot files for the BBC Micro cassette version of Elite\n\nThis folder contains the boot file from the original sources f"
},
{
"path": "1-source-files/images/README.md",
"chars": 569,
"preview": "# Image binaries for the BBC Micro cassette version of Elite\n\nThis folder contains the image binaries from the original "
},
{
"path": "1-source-files/main-sources/README.md",
"chars": 972,
"preview": "# Annotated source code for the BBC Micro cassette version of Elite\n\nThis folder contains the annotated source code for "
},
{
"path": "1-source-files/main-sources/elite-bcfs.asm",
"chars": 8107,
"preview": "\\ ******************************************************************************\n\\\n\\ BBC MICRO CASSETTE ELITE BIG CODE F"
},
{
"path": "1-source-files/main-sources/elite-build-options.asm",
"chars": 90,
"preview": "_VERSION=1\n_VARIANT=3\n_REMOVE_CHECKSUMS=FALSE\n_MAX_COMMANDER=FALSE\n_DISC=TRUE\n_PROT=FALSE\n"
},
{
"path": "1-source-files/main-sources/elite-disc.asm",
"chars": 2419,
"preview": "\\ ******************************************************************************\n\\\n\\ BBC MICRO CASSETTE ELITE DISC IMAGE"
},
{
"path": "1-source-files/main-sources/elite-loader.asm",
"chars": 125138,
"preview": "\\ ******************************************************************************\n\\\n\\ BBC MICRO CASSETTE ELITE GAME LOADE"
},
{
"path": "1-source-files/main-sources/elite-readme.asm",
"chars": 1997,
"preview": "\\ ******************************************************************************\n\\\n\\ BBC MICRO CASSETTE ELITE README SOU"
},
{
"path": "1-source-files/main-sources/elite-source.asm",
"chars": 1480879,
"preview": "\\ ******************************************************************************\n\\\n\\ BBC MICRO CASSETTE ELITE MAIN GAME "
},
{
"path": "1-source-files/original-sources/$.DEFEDIT.inf",
"chars": 31,
"preview": "$.DEFEDIT 000000 000000 000557\n"
},
{
"path": "1-source-files/original-sources/$.DEFEDIT.txt",
"chars": 1310,
"preview": " 10 REM DEFSHIP EDITOR\n 20 MODE7:@%=3\n 30 PROCPR(\" DEFSHIP EDITOR\",3,1)\n 40 INPUT''\"Input filename of DE"
},
{
"path": "1-source-files/original-sources/$.DEFGEN.inf",
"chars": 31,
"preview": "$.DEFGEN 000000 000000 0006BB\n"
},
{
"path": "1-source-files/original-sources/$.DEFGEN.txt",
"chars": 1710,
"preview": ">LIST\n\r 10 REM DEFSHIP EDITOR\n\r 20 MODE7:@%=3:*OPT1,2\n\r 30 PROCPR(\" DEFSHIP GENERATOR\",3,1)\n\r 40 INPUT''\"I"
},
{
"path": "1-source-files/original-sources/$.DEFTRAN.inf",
"chars": 31,
"preview": "$.DEFTRAN 000000 000000 0007C4\n"
},
{
"path": "1-source-files/original-sources/$.DEFTRAN.txt",
"chars": 1978,
"preview": ">LIST\n\r 10 REM DEFSHIP EDITOR\n\r 20 MODE7:@%=3\n\r 30 PROCPR(\" DEFSHIP EDITOR\",3,1)\n\r 40 INPUT''\"Input file"
},
{
"path": "1-source-files/original-sources/$.DIALGEN.inf",
"chars": 31,
"preview": "$.DIALGEN 000000 000000 00048D\n"
},
{
"path": "1-source-files/original-sources/$.DIALGEN.txt",
"chars": 1124,
"preview": " 10 REM Generate new DIALS bitdump\n 20 \n 30 XMAX=68*4:YMAX=18*4: REM Ellipse\n 32 CYC=53*4:CXC=196*4:RD=11*4\n 3"
},
{
"path": "1-source-files/original-sources/$.ELITEA.inf",
"chars": 31,
"preview": "$.ELITEA 000000 000000 002FA2\n"
},
{
"path": "1-source-files/original-sources/$.ELITEA.txt",
"chars": 12047,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 5GOTO120\n 20REM ELITE <A>\n 30MODE7:VDU28,0,23,39,19\n 40L"
},
{
"path": "1-source-files/original-sources/$.ELITEB.inf",
"chars": 31,
"preview": "$.ELITEB 000000 000000 002D20\n"
},
{
"path": "1-source-files/original-sources/$.ELITEB.txt",
"chars": 11373,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 20REM ELITE <B>\n 1000O%=W%:H%=L%+P%-C%:Q%=FALSE\n"
},
{
"path": "1-source-files/original-sources/$.ELITEC.inf",
"chars": 31,
"preview": "$.ELITEC 000000 000000 002F56\n"
},
{
"path": "1-source-files/original-sources/$.ELITEC.txt",
"chars": 11942,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 20REM ELITE <C>\n 1000"
},
{
"path": "1-source-files/original-sources/$.ELITED.inf",
"chars": 31,
"preview": "$.ELITED 000000 000000 002E49\n"
},
{
"path": "1-source-files/original-sources/$.ELITED.txt",
"chars": 11684,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/$.ELITEE.inf",
"chars": 31,
"preview": "$.ELITEE 000000 000000 002DDB\n"
},
{
"path": "1-source-files/original-sources/$.ELITEE.txt",
"chars": 11593,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/$.ELITEF.inf",
"chars": 31,
"preview": "$.ELITEF 000000 000000 002ED2\n"
},
{
"path": "1-source-files/original-sources/$.ELITEF.txt",
"chars": 11828,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/$.ELITEG.inf",
"chars": 31,
"preview": "$.ELITEG 000000 000000 002EDB\n"
},
{
"path": "1-source-files/original-sources/$.ELITEG.txt",
"chars": 11757,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/$.ELITES.inf",
"chars": 31,
"preview": "$.ELITES 000000 000000 002B49\n"
},
{
"path": "1-source-files/original-sources/$.ELITES.txt",
"chars": 10913,
"preview": " 1 DISC=TRUE:PROT=(NOT DISC)\n 5REM Source Code for ELITE (the loader)\n 7MODE7\n 8HIMEM=&4000:CODE=&4000:DIMT"
},
{
"path": "1-source-files/original-sources/$.FINDSC2.inf",
"chars": 31,
"preview": "$.FINDSC2 000000 000000 0004FB\n"
},
{
"path": "1-source-files/original-sources/$.FINDSC2.txt",
"chars": 1237,
"preview": " 10 REM FIND SOURCE\n 20 R=&76:S=&77:T=&78:J=&79:Q=&7A:FL=&7B\n 40 ctl=&70:add=&74:OSARGS=&FFDA:page=24:OSWRCH=&FFEE"
},
{
"path": "1-source-files/original-sources/$.GENTOK.inf",
"chars": 31,
"preview": "$.GENTOK 000000 000000 000931\n"
},
{
"path": "1-source-files/original-sources/$.GENTOK.txt",
"chars": 2314,
"preview": " 10 REM Generate TOKENS file\n 20 words=OPENOUT\"WORDS9\"\n 30 ONERRORGOTO150\n 40 REPEATREADA$:FORN%=1TOLENA$:CH%=AS"
},
{
"path": "1-source-files/original-sources/$.GETBIT.inf",
"chars": 31,
"preview": "$.GETBIT 000000 000000 0000A1\n"
},
{
"path": "1-source-files/original-sources/$.GETBIT.txt",
"chars": 141,
"preview": " 10 *LOAD :2.O.NDIALS 7600\n 20 *LOAD O.DIALSHP 6800\n 30 FORJ%=0TO255STEP4:!(J%+&7EC0)=!(J%+&6F00):NEXT\n 40 *SAVE"
},
{
"path": "1-source-files/original-sources/$.MAKER.inf",
"chars": 31,
"preview": "$.MAKER 000000 000000 00023F\n"
},
{
"path": "1-source-files/original-sources/$.MAKER.txt",
"chars": 543,
"preview": " 5 HIMEM=&7A00\n 10 REM Create an ELITE software protected file set on cassette\n 12 ON ERROR GOTO 500\n 20 *DISC"
},
{
"path": "1-source-files/original-sources/$.SBLOCK.inf",
"chars": 31,
"preview": "$.SBLOCK 000000 000000 0003E1\n"
},
{
"path": "1-source-files/original-sources/$.SBLOCK.txt",
"chars": 942,
"preview": " 5REM Prepare DEFAULT and UNIV and serve with WORDS9\n 7MODE0\n 10NA%=&780:POW=15:K%=&C00:NI%=36\n 100GOSUB 1000\n "
},
{
"path": "1-source-files/original-sources/$.SFTPROT.inf",
"chars": 31,
"preview": "$.SFTPROT 000000 000000 000215\n"
},
{
"path": "1-source-files/original-sources/$.SFTPROT.txt",
"chars": 508,
"preview": " 10 REM SOFTWARE PROTECTION\n 20 REM USING OSFILE\n 25 FILEV=&212:IRQ2=&206:BFLAG=&3CA:USVIA=&FE60\n 30 P%=&C00:[\n "
},
{
"path": "1-source-files/original-sources/$.SHPPRTE.inf",
"chars": 31,
"preview": "$.SHPPRTE 000000 000000 000640\n"
},
{
"path": "1-source-files/original-sources/$.SHPPRTE.txt",
"chars": 1553,
"preview": " 1REM Prepare DIALSHP and SHIPS\n 2MODE1\n 5D%=&5490\n 10HIMEM=&2000\n 18*LOAD DMPDIAL\n 20READN%:C%=D%:B%=&7"
},
{
"path": "1-source-files/original-sources/$.TOKPRI.inf",
"chars": 31,
"preview": "$.TOKPRI 000000 000000 0004B1\n"
},
{
"path": "1-source-files/original-sources/$.TOKPRI.txt",
"chars": 1200,
"preview": ">LIST\n\r 100GOSUB1000\n\r 110FORA%=96TO125:PRINTA%\" \";:CALLTT27:PRINT:NEXT\n\r 120FORA%=129TO255:PRINTA%\" \";:CALLTT27:PRIN"
},
{
"path": "1-source-files/original-sources/$.UNPACK.inf",
"chars": 31,
"preview": "$.UNPACK 000000 000000 00045D\n"
},
{
"path": "1-source-files/original-sources/$.UNPACK.txt",
"chars": 1078,
"preview": " 10 REM Unpack an ELITE security number\n 20 REM This works for either disc or tape copies\n 30 REM This program "
},
{
"path": "1-source-files/original-sources/A.GENTOK.inf",
"chars": 31,
"preview": "$.GENTOKA 000000 000000 000B18\n"
},
{
"path": "1-source-files/original-sources/A.GENTOK.txt",
"chars": 2799,
"preview": " 10 REM Generate TOKENS file\n 20 words=OPENOUT\"WORDS9\"\n 30 ONERRORGOTO150\n 40 REPEATREADA$:FORN%=1TOLENA$:CH%=AS"
},
{
"path": "1-source-files/original-sources/O.ELITED.inf",
"chars": 31,
"preview": "$.ELITEDO 000000 000000 002E8C\n"
},
{
"path": "1-source-files/original-sources/O.ELITED.txt",
"chars": 11750,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/README.md",
"chars": 233,
"preview": "# Original source code for the BBC Micro cassette version of Elite\n\nThis folder contains the original source code for th"
},
{
"path": "1-source-files/original-sources/S.BCFS.inf",
"chars": 31,
"preview": "S.BCFS 000000 000000 000380\n"
},
{
"path": "1-source-files/original-sources/S.BCFS.txt",
"chars": 857,
"preview": " 10REM Prepare the Big Code File\n 20 \n 25REM ELTcode\n 30 \n 35REM (a provisional name, ahem)\n 4"
},
{
"path": "1-source-files/original-sources/S.GENTOK.inf",
"chars": 31,
"preview": "$.GENTOKS 000000 000000 000B1F\n"
},
{
"path": "1-source-files/original-sources/S.GENTOK.txt",
"chars": 2806,
"preview": " 10 REM Generate TOKENS file\n 20 words=OPENOUT\"WORDS9\"\n 30 ONERRORGOTO150\n 40 REPEATREADA$:FORN%=1TOLENA$:CH%=AS"
},
{
"path": "1-source-files/original-sources/S.STEST.inf",
"chars": 31,
"preview": "$.STEST 000000 000000 0012D3\n"
},
{
"path": "1-source-files/original-sources/S.STEST.txt",
"chars": 4705,
"preview": " 5REM Source Code for Loader\n 10C%=&F40:S%=C%:L%=&1128:D%=&563A:LC%=&6000-C%:svn=&7FFD\n 20MOS=S%+8:TRTB%=4\n 100D"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEA.txt",
"chars": 12047,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 5GOTO120\n 20REM ELITE <A>\n 30MODE7:VDU28,0,23,39,19\n 40L"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEB.txt",
"chars": 11382,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 20REM ELITE <B>\n 1000O%=W%:H%=L%+P%-C%:Q%=FALSE\n"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEC.txt",
"chars": 11935,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 20REM ELITE <C>\n 1000"
},
{
"path": "1-source-files/original-sources/text-sources/ELITED.txt",
"chars": 11693,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEE.txt",
"chars": 11593,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEF.txt",
"chars": 11827,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "1-source-files/original-sources/text-sources/ELITEG.txt",
"chars": 11757,
"preview": " 1GOTO20\n 2*L.ELITEB\n 3GOTO20\n 4*L.ELITEA\n 6*L.ELITEC\n 7GOTO20\n 8*L.ELITED\n 9GOTO20\n 10*L.ELIT"
},
{
"path": "2-build-files/README.md",
"chars": 867,
"preview": "# Build files for the BBC Micro cassette version of Elite\n\nThis folder contains support scripts for building the BBC Mic"
},
{
"path": "2-build-files/crc32.py",
"chars": 4248,
"preview": "#!/usr/bin/env python\n#\n# ******************************************************************************\n#\n# ELITE VERIF"
},
{
"path": "2-build-files/elite-checksum.py",
"chars": 6764,
"preview": "#!/usr/bin/env python\n#\n# ******************************************************************************\n#\n# CASSETTE EL"
},
{
"path": "2-build-files/elite-decrypt.py",
"chars": 4934,
"preview": "#!/usr/bin/env python\n#\n# ******************************************************************************\n#\n# CASSETTE EL"
},
{
"path": "2-build-files/mktibet-0.3.php",
"chars": 15388,
"preview": "<?php\n\n/*\n * Quadbike 2\n * Copyright (C) 2023 'Diminished'\n\n * This program is free software; you can redistribute it"
},
{
"path": "2-build-files/tibetuef-0.8.php",
"chars": 61222,
"preview": "<?php\n\n/*\n * Quadbike 2\n * Copyright (C) 2024 'Diminished'\n\n * This program is free software; you can redistribute it"
},
{
"path": "3-assembled-output/README.md",
"chars": 367,
"preview": "# Assembled output for the BBC Micro cassette version of Elite\n\nThis folder contains the output binaries from the build "
},
{
"path": "3-assembled-output/README.txt",
"chars": 230,
"preview": "\n\r---------------------------------------\n\rAcornsoft Elite\n\r\n\rVersion: BBC Micro cassette\n\rVariant: Stairway to Hell cas"
},
{
"path": "3-assembled-output/compile.txt",
"chars": 413354,
"preview": ".ZP\n 0000\n.RAND\n 0000\n.TRTB%\n 0004\n.T1\n 0006\n.SC\n 0007\n.SCH\n 0008\n.XX16\n 0009\n.P\n 001B\n."
},
{
"path": "4-reference-binaries/README.md",
"chars": 725,
"preview": "# Reference binaries for the BBC Micro cassette version of Elite\n\nThis folder contains the binaries from the original so"
},
{
"path": "5-compiled-game-discs/README.md",
"chars": 559,
"preview": "# Compiled game discs for the BBC Micro cassette version of Elite\n\nThis folder contains the SSD disc images for the BBC "
},
{
"path": "Makefile",
"chars": 4731,
"preview": "BEEBASM?=beebasm\nPYTHON?=python\nPHP?=php\n\n# A make command with no arguments will build the source disc variant with\n# e"
},
{
"path": "README.md",
"chars": 32664,
"preview": "# Fully documented source code for the cassette version of Elite on the BBC Micro\n\n<details>\n<summary>Links to my other "
},
{
"path": "make.bat",
"chars": 155,
"preview": "@echo off\nSETLOCAL\nSET BEEBASM=C:\\Users\\user\\bin\\beebasm.exe\nSET PYTHON=C:\\Users\\user\\AppData\\Local\\Microsoft\\WindowsApp"
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the markmoxon/elite-beebasm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 91 files (2.3 MB), approximately 600.2k tokens, and a symbol index with 71 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.