Skip to navigation


Generating code comparisons for Elite

Using a library repository to compare the different versions of Elite

The comparison section of the Elite website is a good example of something that would be pretty difficult to create without content generation. It compares the source code for all five Acornsoft versions of Elite on a line-by-line basis, showing all the variations side-by-side, along with indexes and commentary on each variation. Creating this content by hand would be almost impossible, so it's no surprise that this section is generated by a Python script.

If you've managed to wade through the article on generating source code for Elite, then you'll already know that the library repository is marked up with IF statements that define which code appears in which version of Elite. The comparison section is essentially a web-based version of this information, showing side-by-side code comparisons that reflect the BeebAsm conditional blocks in the library source code.

Consider this code variation that appears in part 2 of the BR1 routine:

  IF _CASSETTE_VERSION OR _ELECTRON_VERSION

   LDA #147            \ Call TITLE to show a rotating Mamba (#3) and token
   LDX #3              \ 147 ("PRESS FIRE OR SPACE,COMMANDER.{crlf}{crlf}"),
   JSR TITLE           \ returning with the internal number of the key pressed
                       \ in A

  ELIF _DISC_DOCKED OR _ELITE_A_VERSION

   LDA #7              \ Call TITLE to show a rotating Krait (#KRA) and token
   LDX #KRA            \ 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER.{cr}
   JSR TITLE           \ {cr}"), returning with the internal number of the key
                       \ pressed in A

  ELIF _6502SP_VERSION

   LDA #7              \ Call TITLE to show a rotating Asp Mk II (#ASP) and
   LDX #ASP            \ token 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER.
   JSR TITLE           \ {cr}{cr}"), returning with the internal number of the
                       \ key pressed in A

  ELIF _MASTER_VERSION

   LDA #7              \ Call TITLE to show a rotating Cougar (#COU) and token
   LDX #COU            \ 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER.{cr}
   LDY #100            \ {cr}"), with the ship at a distance of 100, returning
   JSR TITLE           \ with the internal number of the key pressed in A

  ELIF _C64_VERSION

   LDA #7              \ Call TITLE to show a rotating Adder (#ADA) and token
   LDX #ADA            \ 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER.{cr}
   LDY #48             \ {cr}"), with the ship at a distance of 48, returning
   JSR TITLE           \ with the internal number of the key pressed in A

  ELIF _APPLE_VERSION

   LDA #7              \ Call TITLE to show a rotating Sidewinder (#SH3) and
   LDX #SH3            \ token 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER.
   LDY #75             \ {cr}{cr}"), with the ship at a distance of 75,
   JSR TITLE           \ returning with the internal number of the key pressed
                       \ in A

  ENDIF

You can see this difference in the comparison section of the website, where the four variations are shown side-by-side (only Acornsoft versions are included in the code comparison section, so the Elite-A, Commodore 64 and Apple II parts are ignored). This view is generated by the same create-disassembly-websites.py script that generates the code sections of the website, but this time it ingests the content directly from the library repository, rather than from the source code repositories.

The above example isn't quite the whole story, though, as each IF-block in the library repository is also tagged with meta-information about that difference, which I've left out of the above example to keep things simple. In reality, the first line of each conditional block has a comment after the IF statement that contains a description of the variation, and this is shown not only in the comparison page above, but also in the summary pages like the compare code for features of standard Elite page - you can see the information for this variation at the top of that page.

So the first line of the above conditional actually looks like this:

  IF _CASSETTE_VERSION OR _ELECTRON_VERSION \ Standard: On the second title
  page (the one that says "Press Space Or Fire,Commander"), the cassette and
  Electron versions show a rotating Mamba, the disc version shows a rotating
  Krait, the 6502SP version shows a rotating Asp Mk II, and the Master
  version shows a rotating Cougar

The first part of the comment, up to the colon, defines the category of this variation, which determines which index page it appears on (in this case it's a standard change, which means it's related to a feature in the standard versions of Elite). The rest of the comment is the description, which appears on the index page and in the code comparison.

Variations can also be grouped together, in case a difference in functionality is split across multiple variations in the same routine. For example, the first variation in the ANGRY routine in the compare code for features of enhanced Elite page is a grouped variation. If you click through to the ANGRY routine comparison itself, then you can see that variations 1 and 3 are grouped, and variation 3 contains a link up to variation 1. Variations are grouped together in the library source by adding the group identifier (a letter) after the category, like this in the first variation:

  IF _CASSETTE_VERSION \ Enhanced: Group A: In the enhanced versions,
  attacking an innocent bystander (i.e. a ship that has bit 5 of the NEWB
  flags set) will annoy the space station

and like this in subsequent variations:

  IF _CASSETTE_VERSION \ Enhanced: See group A

Let's now take a more detailed look at how the script creates the comparison pages.

A deeper look at the comparison process
---------------------------------------

As mentioned in the article on generating source code repositories for Elite, the generation of the comparison part of the website is implemented by a single line in the generate-elite.sh shell script:

  python3 create-disassembly-websites.py compare

The "compare" argument tells the script to generate the comparison part of the site. Here's a summary of what the script does, along with links to relevant examples of the output on the Elite website:

  • Print "Comparison section" to the terminal.
  • Call create_folder() to create the folders we need to hold the comparison section of the website.
  • Print "Analysing files for comparison" to the terminal.
  • Call analyse_files_for_compare() to ingest the source code library and populate the global variables includes_in_versions{} and all_includes{}.
  • Call output_compares_indexes() to create the indexes of variations and code usage, such as the list of all shared code that contains variations.
  • Call output_compare_category_index() to create the summary of code variations in each category, such as the list of comparisons of features of standard Elite.
  • Call output_compare_other_categories_index() to create the summary of minor variations between versions.
  • Print "Writing comparison pages" to the terminal.
  • Call output_compare_version_page() to create the individual code variation pages, such as the version analysis of BLINE.
  • Print "Writing menus" to the terminal.
  • Call output_compare_menu() to create the navigation_compare.php include file containing the left-hand navigation for the comparison section.

Global variables
----------------

Here's a list of important global variables that are used in the comparison script:

  • includes_in_versions{} = a list of include files for each version of Elite
  • all_includes{} = detailed data on every single include file in the library repository

These are populated by process_compare_line() > add_compare_include(), and the script writes their contents into the includes_in_versions.txt and all_includes.txt files in the disassembly-website-generator/debug folder.

Call hierarchy
--------------

Here's a call hierarchy of the above processes, which will help you orientate yourself if you want to look through the script. This is not a breakdown of each routine's actions, it's just a list of function usage in the script, so it's more of a map for your own investigations rather than a full explanation.

In the following, a + indicates a routine that is called from multiple places, while a - indicates this subroutine is only called once in the whole program.

RoutineDetails
+ create_folder()
Create skeleton website folders
+ analyse_files_for_compare()
Analyse all source code files for comparisons
 + process_compare_file()
Process an individual source code file for comparisons
  + strip_elite_a()
Strip platforms defined in omit_from_compare[] from comparison
  + process_compare_line()
Process an individual source code line for comparisons
   - add_compare_include()
Add details of each included file to all_includes{}
- output_compares_indexes()
Create the indexes of variations and code usage
 + start_html()
Output the start of the HTML page
 + output_next_prev()
Output next/prev links for page
 - add_code_link()
Create a link to a specific version of Elite
 + end_html()
Output the end of the HTML page
- output_compare_category_index()
Create the summary of variations in each category
 + start_html()
Output the start of the HTML page
 + output_next_prev()
Output next/prev links for page
 + end_html()
Output the end of the HTML page
- output_compare_other_categories_index()
Create the summary of minor variations between versions
 + start_html()
Output the start of the HTML page
 + output_next_prev()
Output next/prev links for page
 + get_url_for_code_page()
Fetch the URL for a page containing source code
  - get_stage()
Fetch the stage (e.g. loader, text_tokens) for an include
 - tag_comments_as_ul()
Extract the tag comments for a code difference as HTML
 - tag_comments_for_single_platform_variation()
Extract the tag comments for a single-platform difference as HTML
 + end_html()
Output the end of the HTML page
- output_compare_version_page()
Create the code variation pages for each routine
 + start_code_html()
Output the start of the HTML code page
 - get_links_for_compared_versions()
Fetch a list of all versions containing the routine being compared
  + get_url_for_code_page()
Fetch the URL for a page containing source code
   - get_stage()
Fetch the stage (e.g. loader, text_tokens) for an include
  + convert_version_to_full_name_comma_list()
Convert DISC_DOCKED to "Disc (docked)" etc.
 - convert_versions_to_link_list()
Convert an array of version names to an HTML list
 - output_compare_line()
Output marked-up HTML for a compared line of code
  + tidy_source_header_line()
Add markup to a source code header line
  - replace_workspace_include_with_variable()
In compared workspaces, just display variable names and no comments
  - append_multi_version_compare_line()
Add a compared code line to the compare buffer for output later
  + output_buffered_compare_line()
Output the compare code buffer
 - output_multi_version_section()
Output HTML for a compared code block with multiple versions
  - convert_versions_to_list()
Create a comma-separated list of versions that do not contain a specific comparison
   + convert_version_to_full_name_comma_list()
Convert DISC_DOCKED to "Disc (docked)" etc.
  - add_links_to_compared_routines()
Create a comma-separated list of versions that contain a specific comparison
 + output_buffered_compare_line()
Output and flush the compare buffer
 + end_code_html()
Output the end of the HTML code page
- output_compare_menu()
Create the left nav for the comparison section
 - add_compare_article()
Add compare page to routines{}

Finally, as mentioned in the article on generating websites from source code, there is an additional call made to the analyse_files_for_compare() routine when generating the source code pages for the individual versions on the Elite site. This ensures that the global variables includes_in_versions{} and all_includes{} are populated correctly, so we can use them to add comparison links into the headers of each routine in the main source code section of the site. This isn't needed for Aviator, Revs and Lander, as those sites don't have a comparison section.