Skip to navigation

About my disassembly diary for The Sentinel

How I documented The Sentinel over seven long months

People often ask me how I create my disassemblies, so for my last project I kept notes; a lot of notes. Welcome to my disassembly diary for The Sentinel.

Between September 2025 and April 2026, I disassembled and documented Geoff Crammond's epic BBC Micro game, The Sentinel. I wrote down pretty much every step in the entire process, starting with the original game disc and ending, some seven months later, with fully documented source code and over 50 deep dive articles, all lovingly presented via a comprehensive website and a buildable GitHub repository.

The Sentinel on the BBC Micro screenshot

You can see my diary for the whole project below. You can click on a date to read the diary entry for that day, or you can see all of the code changes from that day by clicking on the "Code" link to see the GitHub diffs.

The diary entries are very terse; they're technical notes, not Samuel Pepys. They contain the barest of information and aren't exactly a riveting read, but as a body of work they comprehensively describe the project. They also link through to the documented source code, so not only can you see the in-progress disassembly in GitHub, but you can compare it with the end result as well.

On top of the diary entries, I've also written about the tools that I use for my disassemblies. Spoiler alert: it's all extremely manual and the tooling is about as primitive as it can be. This is intentional.

You can also see the project progression below, highlighted in 5% increments. I like to keep track of progress in minute detail; see the article on keeping track of progress for more about the tools and philosophies behind this.

There are some more notes after the table that you might find useful, but for now, here's a summary of every day in my disassembly diary.

Diary entryDescriptionDiffs
2025-09-03Get game discs, extract binaries, create repository, create skeleton build process, create simple py8dis disassembly, start identifying code blocksCode
2025-09-04Implement loader unpacking with COPYBLOCK, finish identifying code blocks, copy py8dis output into main source code file, switch to using text editor, add headers to sections (source start, configuration variable, workspaces, code, saving)Code
2025-09-05Apply house style to OS calls, identify subroutine entry points that aren't in the sub_* format (e.g. JSR destinations, labels after RTS), convert all other labels to five characters (letter + hex), insert subroutine headers before all sub_ labels, create an empty progress spreadsheet, start at 0% progressCode
2025-09-06Identify maths lookup tables using Excel, convert them from data blocks into FOR loops, convert some of the calculated labels into Lxxxx labelsCode
2025-09-07Document the Entry routine (the first documented routine!)Code
2025-09-09Document ConfigureMachine and ClearMemory, identify main title loop, start trying to identify subroutines called by main title loop (e.g. SetColourPalette, ResetVariables, DrawTitleScreen, DrawObject, SpawnCharacter3D, SpawnSecretCode3D), realise that some code is the same as in Revs (Multiply8x8, GetAngleInRadians)Code
2025-09-10Identify more Revs routines (GetRotationMatrix, Multiply8x16, Multiply16x16) and copy comments from Revs, reach 5% progress, identify and document more Revs code (Absolute16Bit, Negate16Bit, ScanKeyboard, parts of Divide16x16), add arctan and sin labelsCode
2025-09-11Document GetAngleFromCoords, GetHypotenuse, tanHalfAngle and MultiplyCoords, lay out zero page and workspace variables, identify GetNextSeedNumberCode
2025-09-14Document GetNextSeedNumber, identify PrintTextToken, insert text token labels, reach 10% progressCode
2025-09-15Document all the text tokens and text token routines, document ReadKeyboard, ReadCharacter and EnableKeyboardCode
2025-09-16Document PrintNumber, PrintDigit, Print2DigitBCD, PrintLandscapeNum, ReadNumber, PrintInputBuffer, StringToNumber, DigitToNumber, InitialiseSeeds, start looking at GenerateLandscape and GetTileData, identify tileData table, reach 15% progress, work out structure of tileData tableCode
2025-09-17Document ProcessTileData, identify SmoothTileCorners and SmoothTileDataCode
2025-09-18Start documenting SmoothTileCorners and SmoothTileData, more thinking around stripDataCode
2025-09-19Finish documenting SmoothTileCorners, work out obfuscated jump code for JumpToPreviewCode
2025-09-22Finish documenting GenerateLandscape, clarify "tile corner" vs "tile" in commentaryCode
2025-09-23Start analysing SpawnEnemies, GetEnemyCount and AddEnemiesToTiles, work out landscape colour variablesCode
2025-09-24Continue analysing AddEnemiesToTiles, document GetHighestTiles, identify maxAltitude, xTileMaxAltitude and zTileMaxAltitude, reach 20% progress, document GetObjectNumber, identify objectType, objectTypes and objectFlags, work out that there are 64 objects, start looking at GetTilesAtAltitudeCode
2025-09-26Document GetTilesAtAltitude, identify xObject, yObjectHi and zObjectCode
2025-09-30Split out SpawnPlayer, SpawnTrees and CheckSecretCode, document SpawnObjectOnTile, work out data packing in tileData, rename xObject, yObjectHi and zObject, work out object types 3 and 6Code
2025-10-01Realise y-coordinate is 16-bit so we have yObjectHi and yObjectLo, revamp terminology to talk about altitude rather than height, identify SpawnObjectBelow, finish documenting SpawnPlayerCode
2025-10-02Document SpawnTrees, split off CheckSecretCode parts 1 and 2, start documenting CheckSecretCode, identify GetNextSeedAsBCD and FinishLandscapeCode
2025-10-03Finish documenting CheckSecretCode, reach 25% progress, document PreviewLandscape, starting adding "???" to unknownsCode
2025-10-04Realise that seed numbers aren't random but are predictable, rename routines accordinglyCode
2025-10-07Mention other sources of information (Simon Owen, Level 7), identify objRotationSpeed, objRotationTimer and objectYawAngle, finish documenting PlayGame, document FlushSoundBuffersCode
2025-10-08Separate landscape and game data where memory is reused, add comments to workspace variables, identify objectPitchAngleCode
2025-10-09Document GetSineAndCosine and DivideBy16, thoughts on GetRotationMatrix, document key logger, identify sights-related variables, start analysing GetSightsVectorCode
2025-10-10Start analysing SetCrackerSeed and CorruptSecretCode, work out that objectType can contain key presses, add speculative names to sights routinesCode
2025-10-13Convert "tile slope" terminology to "tile shape"Code
2025-10-14Identify key processing routines, categorise sound routines, document GetPlayerEnergyBCD, finish documenting FinishLandscape, first attempt to understand FocusOnKeyAction, start looking at DrainObjectEnergyCode
2025-10-15Identify MakeSoundEnvelope, DefineEnvelope, envelopeData and soundNumberData, document UpdateIconsScanner and DrawIcon, identify iconBuffer and iconRowAddr, document DisplayIconBufferCode
2025-10-16Add another COPYBLOCK to source, remove unused labels, reach 30% progress, finish identifying object numbers, identify enemyData variables, document ProcessVolumeKeys and ProcessPauseKeys, identify UpdateScannerNow and GetIconRowAddressCode
2025-10-17Document PerformHyperspace, UpdatePlayerEnergy and DeleteObject, rename sentinelHasWon, reach one-third progressCode
2025-10-19Rename numberOfEnemies and maxNumberOfEnemies, start analysing IRQHandlerCode
2025-10-20Document duplicate tile data code in GetTileViewAngles, document use of SHEILA addresses for hardware scrolling, document ResetScreenAddressCode
2025-10-21Identify screen-scrolling routines at ScrollPlayerView and ShowScreenBuffer, document ShowBufferBlock, ShowBufferRow and ShowBufferColumn, reach 35% progressCode
2025-10-22Document ScrollPlayerView, DisplayViewBuffer, SetNumberOfScrolls and SetViewBufferAddr, musings on screen buffer memory shapeCode
2025-10-23Document ClearIconsScanner, ponder focusOnKeyAction, identify uTurnStatus, ShowGameOverScreen and playerHasMovedTile, start documenting ProcessGameplayCode
2025-10-24Start to analyse viewingObject and ProcessActionKeys, get confused about screen buffer addressesCode
2025-10-25Document ProcessGameplayCode
2025-10-26Identify FocusOnKeyAction and hyperspaceEndsGameCode
2025-10-28Document GetSightsVector and GetVectorForAngles, identify enemyEnergy, document UpdateScanner, scannerUpdate and lastScannerState, identify ClearScreen and DrawLandscapeView, first look at PlayMusic, finish first pass of MainGameLoop, reach 40% progressCode
2025-10-29Start analysing InitialiseSights and DrawSights, document SetSightsAddress, identify doNotDrawSightsCode
2025-10-30Document sightsByte variables and RemoveSights, start documenting DrawSightsCode
2025-10-31Finish documenting DrawSightsCode
2025-11-03Analyse soundEffect and gameOverSoundPitch, document PlayMusic, ProcessSound, ProcessMusic, musicCounter and musicData, reach 45% progressCode
2025-11-04Document ClearScreen, identify screenRowAddrHi and screenRowAddrLo, document FillScreen and GetRandomNumberCode
2025-11-05Document DrawRandomDots, SetScannerAndPause and UpdateScannerNowCode
2025-11-06Document GetVerticalDelta and GetHorizontalDelta, create a hierarchy sheetCode
2025-11-10Start analysing GetTileAltitude and CheckForTileCentre, identify xCoord, yCoord and zCoord, musings on naming of top, high, low and bottom bytesCode
2025-11-11Identify GetObjectCoords, extract CheckSecretStash from GetRowVisibility, start documenting GetRowVisibility, identify GetTileVisibility, reach 50% progressCode
2025-11-12Finish documenting GetRowVisibility, identify considerObjects, finish document GetTileVisibility and tileVisibilityCode
2025-11-13Document part 1 of DrawLandscapeView, revert incorrect variable names for shared locations, identify viewingArcRightYaw, quadrantOffset, quadrantOffsets, xTileViewer and zTileViewer, document GetHypotenuseAngleCode
2025-11-14Get stuck on GetPitchAngleDeltaCode
2025-11-16Identify GetTileViewEdges, CheckPreviousTile, CheckNextTile, GetTileViewAngles, GetPitchAngleDelta and GetTileViewAngles, identify all drawView variables, tileIsOnScreen, pitchDeltaLo, pitchDeltaHi and drawingTableOffsetCode
2025-11-17Finish documenting GetPitchAngleDelta, reach 55% progress, add progress graphCode
2025-11-18Identify xTileViewLeft, xTileViewRight, xTileViewLeftEdge, xTileViewRightEdge and xTileLeftPrevious, document GetTileViewAngles, GetTileViewEdges (with issues) and DrawLandscapeRowCode
2025-11-19Identify DrawObject, DrawPolygon, DrawObjectStack, DrawTileAndObjects and DecayScreenToBlack, musings on field of view angles and similarities to Revs, document DrawTileAndObjects, extract cracker code from DrawFlatTile, document part 2 of DrawLandscapeViewCode
2025-11-20Categorise scanner and screen buffer code, document part 3 of DrawLandscapeView, identify keepCheckingPanKey, document DecayScreenToBlack, reach 60% progress, document ShowGameOverScreenCode
2025-11-21Musings on the pan angle and projection, identify panAngleToUpdate, screenLeftYawHi and yawAdjustmentLo, rename viewingObjectCode
2025-11-24Create panorama image, document SetSecretStash and CheckSecretStash, identify SetCrackerSeed and CheckCrackerSeedCode
2025-11-25Work out how stripData is used in the anti-cracker code, document CrackerSeed and AlterCrackerSeed, continue documenting DrawFlatTile, identify DrawOneFaceTile, DrawSlopingTile, DrawTwoFaceTile and tileShapeColourCode
2025-11-26Document DrawOneFaceTile, DrawSlopingTile and DrawTwoFaceTile, try (and fail) to work out what screenBufferType is, identify PanLandscapeView, document tileShapeColour, musings on considerObjects, document AddVectorToCoord, identify FollowGazeVectorCode
2025-11-27Document FollowGazeVector, musings on tile shapes, continue documenting GetTileAltitudeCode
2025-11-30Finish documenting GetTileAltitudeCode
2025-12-01Document PanLandscapeView and panAngleToUpdate, reach 65% progressCode
2025-12-02Document screenRowAddr(Hi Lo), bufferRowAddr(Hi Lo), identify colourPixels, pixelsToLeft, pixelsToRight and leftPixels, document screenOrBuffer, discover that my analysis of viewBuffer is all wrong, identify UseRowBuffer, UseColumnBuffer, ConfigureBuffer, FlipBufferType, maxPitchAngle, minPitchAngle, bufferMaxPitch and bufferMinPitch, finally understand viewBufferAddr, more failed attempts to understand focusOnKeyActionCode
2025-12-03Work out left and right row buffers, document DrawObjectStack, reach two-thirds progress, finally settle on names for focusOnKeyAction and previousFocus, continue analysing DrawObject, identify GetObjectAngles, objectToAnalyse, objTypeToAnalyse, objectViewYaw(Hi Lo), objectGazeYaw(Hi Lo), viewType, objectAdjacent(Hi Lo) and objectOpposite(Hi Lo)Code
2025-12-04Identify objPointRange, objPointYaw, objPointHeight, objPointDistance, objPolygonRange, objPolygonPhases and objPolygonDataCode
2025-12-05Document objRobot, objSentry, objTree, objBoulder, objMeanie, objSentinel, objTower and objTextBlock, identify objPolygonAddr(Hi Lo), reach 70% progressCode
2025-12-06Document objPolygonData and polygonColoursCode
2025-12-07Document objPointYaw, objPointPitch and objPointDistanceCode
2025-12-08Improve objPolygonData comments, change terminology to "polygon edges" rather than "polygon lines", document blendPolygonEdges, identify polygonEdgeCount, drawViewAngles, polygonType, drawViewYaw(Hi Lo) and drawViewPitch(Hi Lo), add labels to all the screen buffersCode
2025-12-09Finish documenting DrawObject, document DrawTitleObject, identify SpawnTitleObject, DrawTitleObjects and DrawTitleObject, document yTextViewer, textViewerPitch, xTextViewer, zTextViewer, textViewerYaw, titleOffset, zTitleObject, yTitleObject, titleObjectYaw, titleViewerPitch, titleViewerYaw and SpawnTitleObjectCode
2025-12-10Document DrawTitleView, identify objBlockNumber, document titleText, reach 75% progress, rename screen buffers to final names (screenBufferRow0, screenBufferAddr etc.), tidy up startup code and tackle a number of "???"sCode
2025-12-11Add new multibyte terminology, identify ApplyEnemyTactics, MoveOnToNextEnemy, ApplyTactics, FinishEnemyTactics, ExpendEnemyEnergy, GetObjVisibility, objectHalfWidth and minObjWidth, document GetObjVisibilityCode
2025-12-12Document bufferColumns, CheckObjVisibility, AbortWhenVisible and ExpendEnemyEnergy, start analysing ApplyTactics, identify objTacticsTimer and spawnedMeanieCode
2025-12-13Start analysing CheckEnemyGaze, discover that enemy timers are interrupt drivenCode
2025-12-14Rename enemy timers to start with "enemy", rename enemyTacticTimer, enemyYawStep and enemyMeanieTree, document UpdateEnemyTimers, identify CheckEnemyGaze, targetVisibility, enemyCheckingRobot and treeVisibilityCode
2025-12-15Continue analysing ApplyTactics, identify enemyTargetCode
2025-12-16Identify DrawUpdatedObject and DitherScreenBuffer, document tact25 in ApplyTactics, drawLandscape and ditherObjectSights, identify ConfigureObjBuffer, rename enemyDrainTimer and enemyRotateTimer, document parts 2 and 3 of ApplyTacticsCode
2025-12-17Identify doNotDitherObject, ditherInnerLoop, ditherOuterLoop and yawAdjustmentHi, analyse bufferOrigin(Hi Lo), identify objScreenAddr, document DrawUpdatedObject, reach 80% progress, identify bitMaskDither and randomPixelDitherCode
2025-12-18Document DitherScreenBuffer, FindObjectToDrain and parts 4, 5 and 6 of ApplyTactics, rename enemyVisibility, identify enemyMeanieScan, ScanForMeanieTree, enemyFailTarget, enemyFailCounter and ResetMeanieScan, document ScanForMeanieTree and part 7 of ApplyTacticsCode
2025-12-19Document polygonPoint, finish documenting main variable workspace, reach 85% progress, document GetPlayerDrain, start to analyse GetPolygonLines and DrawPolygonLinesCode
2025-12-20Identify GetPolygonLines and DrawPolygonLines, start documenting GetPolygonLinesCode
2025-12-21Start analysing DrawPolygonLines, identify xPolygonLeft, xPolygonRight, yPolygonBottom and yPolygonTopCode
2025-12-22Document parts 1 and 4 of DrawPolygonLines, identify xBufferRight, xBufferLeft and xBufferWidth, document part 3 of DrawPolygonLines, reach 90% progressCode
2025-12-23Run first spell check, identify polygonGoesRight and polygonGoesLeft, document part 2 of DrawPolygonLinesCode
2025-12-24Rename GetPolygonLines, xPolygonRight and xPolygonLeftCode
2025-12-26Analyse considerObjects, document yAccuracyLo, identify xPolygonPoint(Hi Lo), categorise all headersCode
2025-12-27Document parts 3 and 4 of GetPolygonLinesCode
2025-12-28Identify yEdgeStart(Hi Lo), yEdgeEnd(Hi Lo), xEdgeStart(Hi Lo) and xEdgeEnd(Hi Lo), clarify "polygon lines" and "polygon edges" terminology, identify yEdgeDelta(Hi Lo), xPolygonAddrHi and TracePolygonEdge, document part 5 of GetPolygonLinesCode
2025-12-29Identify ModifyStoringCode, document part 6 of GetPolygonLines, reach 95% progress, start analysing TracePolygonEdge, identify xEdgeDeltaCode
2025-12-30Document parts 2 to 7 of TracePolygonEdgeCode
2025-12-31Document part 8 of TracePolygonEdge, rename SetScannerAndPause, buffersOrigin, bufferOrigin(Hi Lo), xBufferWidth, xBuffersWidth, xBufferRight, xBufferLeft and xBuffersLeft, reach 100% progressCode
2026-01-02Update analysis scripts for the new repositoryCode
2026-01-07Convert the Sentinel repository into a website by adding it to my automation scriptsCode
Content
2026-04-14Write and publish 51 deep divesContent

Notes
=====

Here are some things to bear in mind when exploring my disassembly diary:

  • The links in the right column will take you to a diff comparison view for that day within the project's GitHub repository. If it was a particularly busy day then the GitHub links may not show all of the changes by default; if this happens, you may need to click on the "Load diff" links first.
  • There are a few shorthand terms in the diary that you should know about. When you see something like this:
      sub_C1410 -> SpawnEnemies
    
    then this means that I have changed the name of the sub_C1410 routine to SpawnEnemies. And when you see this kind of thing:
      Start looking at CreateEnemies > sub_C158D.
    
    then this means that I'm analysing the sub_C158D routine that is called from within CreateEnemies (probably by a JSR sub_C158D instruction inside CreateEnemies).
  • All variable and subroutine names in the summary above are the final names that I landed on by the end of the project. Most names went through some kind of change at some point, so to make things consistent and followable, the summary always contains the names in the documented source. In contrast, the diary entries themselves contain the label names at the time of writing, but because these link through to the final documented source code, you can use this to see whether a label's name did change.
  • Finally, please note that this diary is a dump of my thoughts as I worked through The Sentinel, and as such it contains lots of mistakes and dead ends and misinterpretations. Sometimes it's terse; and sometimes it flows and meanders as I try to work things out by writing them down. This diary is all about the journey, rather than the destination; for the latter, see the finished product.

I hope you find it interesting...