Realized in file “calibration.asm”, bank 0, entry “initCalibration1” (with own display loop).
Realized in file “titlePic.asm”, bank1, entry “displayTitlePicture” (loop lable: “displayTitleLoop“).
Desktop – stages
Realized in different files, entry in “mainBank2.asm” at “noTitleTitle” (loops at Titleloop1), each loop round a subroutine in bank0 is called “oneVBTitleStep” – which is realized in file: “desktop.asm”.
Within that file countdown dependend, different desktop stages are called, the stages are defined in a (null terminated) pointer list at “stagesPointer“. The defined different stages as of now are:
Called from the “desktop” (bank2, file “mainBank2.asm” from above title routines), this is divided in two subsections:
b) enter initals
The display is realized in a file called “highscore.asm” in bank1. Entry is at “showHighscoreMusic“, loop label is “edithighscoreinner“.
The editing is realized in a file called “highScoreEdit.asm” in bank1 – which is called after the game over sequence, entry is at “HighScoreTest“, loop label is “HSloop1“
Called from the “desktop” (bank2, file “mainBank2.asm” from above title routines) “doOptions“, realized within that file, loop label: “optionsLoop“.
Achivements (overview list)
Called from the “desktop” (bank2, file “mainBank2.asm” from above title routines): “doAchiements1” (in bank1 – “mainBank1.asm”), loop label: “displayAchievementLoop“.
Called from the “desktop” (bank3, file “mainBank3.asm” from above title routines) “main11“, realized within that file, loop label: “main12“.
Called from the “gameLoopPart0_m” (bank2, file “mainBank2.asm”) “pause_0_0“, realized within file “pause.asm”, loop label: “pauseLoop“.
Called from the level start sequence (bank3, file “mainBank3.asm”) “doShop00“, realized within file “shop.asm”, loop label: “shopLoop“.
Called from the catch bonus sequence (bank2, file “objectBonus.asm”) “startDodger1” (bank3), realized within file “dodger.asm”, loop label: “dodgerIntroLoop” and “MSloop1“.
Called from the catch bonus sequence (bank2, file “objectBonus.asm”) “bandit” (bank1 in file “MainBank1.asm”), which directly calls “bandit2” realized within file “wheel.asm” (bank2), loop label: “extraLoop“.
This is the end of game (100 levels successfully played and last boss fight won).
Called from initLevel in (bank3: mainBank3.asm) levelRollOverAchievementTest (bank1), loop label: “rollOverLoop“.
All four bossfights are called exactly the same (apart from the number 1-4).
Called from initLevel (Bank3 : mainBank3.asm), initBoss1 in file “bossCode1.asm”.
Called from diverse places – “printAchievement” (bank 2 in “mainBank2.asm”) initializes a firework and prints an achievement with firework.
End of game, candle followed by scoring and “accounting”. Called from death sequence in bank3 (“mainBank3.asm”) to “gameOverFinalScoring” (bank1 in “mainBank1.asm”).
Called from diverse places. Bank 0 “help.asm” diverse functions… in general some help functions call the function “showMESSAGE” with different sets of texts.
For a general description what “smartlists” are, please
head over to: vpatrol-ingenuity-part-iii
Within Vectorblade most objects are drawn using smartlists, the way smartlists are realized in code makes it necessary to have different implementation for different scale factors. Within Vectorblade I use 3 different scale factors:
a) scale of 7, this scale factor is used most through out the game, all normal aliens and bonus items are drawn using this scale.
b) scale of 16, this scale is used formost for “vector text” display, e.g. the high score table
c) scale of 50, this scale is mainly used for “big” vectorlists, like the bosses, boss intro sequence and the title image.
The code for smartlists resides in a subdirectory called “smartlist”. Smartlists are used in every bank, and thus are “reused” – or doubled.
Due to the nature of the banksitching, bank0 and bank1 must use a different set of smartlist implementation. These smartlists are “No Shift” smartlists, meaning the drawing of vectors by those smartlists is done WITHOUT the usage of the shift register. Thus I can keep the interrupt flag active while drawing (and thus do no bankswitching within drawing).
All smartlists “drawing” were created using Vide->vecci with:
a) a special Vectorblade mode (checkbox in gui)
b) a special “no shift” mode (checkbox in gui)
Within Vectorblade two different sets of vector fonts are used:
a) for the settings menu
b) for the high score display
This font is defined in file “abc.i”, and in contrast to other vector definitions is defined as a Draw_Vlp compatible list (so scaling is possible). The print routines are defined in bank2 “mainBank2.asm” – “printOneVectorString_D“.
High score font
The highscore font needed to be draw faster (and larger) than the other vector font, since many large letters at the same time were needed. This font is realized as with smartlists. The font is defined in the file “FastABC.asm” (bank 1), the print routine is defined in “highScoreEdit.asm” – “printVectorStringFast“.
Font 5 strings
A font that is only 5 pixels high. Used to print most texts on screen. The font is defined in file “font_5.asm”. The font is available in bank3 (message system), bank2 (achievements/pause), bank1 (achievements) and bank0 (help system/title scroller).
Font 8 Strings
Used to print “READY PLAYER ONE” and “GAME OVER”, font is available in bank0, definitions lie in file “mainBank0.asm”.
The player is “now” drawn in bank 0 – it was moved there due to memory reasons (more space to draw different fighters depending on the equipment of the player). Function to call is: doFighterPrep in “mainBank0.asm”.
Within the player display routine, timers are reduced, this goes for:
multiply timer, shield and scoopy. Together with the player, player “additions” are drawn (shield, armor, scoop, drunken timer) – WITHOUT reseting the vector “cursor”.
The “basics” how I realized “objects” are described in the Release blog, mainly:
The general “technique” is the same – but some optimizations were introduced. Some general words on “object” routines. As an example I’ll use the “bonus” routines, because they are the easiest.
All object structures are defined in the file: “inBothBanks1.i”, the bonus structure looks like:
struct BonusStruct ds Y_POS,1 ; D current position ds X_POS,1 ; ds BEHAVIOUR,2 ; PC ds NEXT_BONUS_OBJECT,2 ; positive = end of list ds TYPE, 1 ds BONUS_ANIM_POSITION, 1 ; low 6 bit - anim, hi 2 bit speed end struct
All objects start with the same 3 entities:
– position (D register)
– behaviour routine pointer (PC register)
– next object
Within “object handling” no subroutines or other stack changing calls are allowed. The stack is always a pointer to such an object structure. All objects are called with a simple:
“puls d, pc“
Which “automatically” jumps to the behaviour routine, and already has y, x position in register D. The object routines continue to directly “moveTo” that location, and during the move most of the object house keeping is done.
The object routine calculates the current animation stage and sets the “smartlist” address (vector drawing list) to the other stack register U.
The behaviour routines always close with calling the draw routines with a
“pulu b, x, pc“
Which sets the first values for vectordrawing and jumps to the draw routines of the smartlist. Most smartlists end with the instructions:
“puls d, pc“
Which automatically continues to the next “object” (or jumps to a place in the main loop, since the last object in the object list always has the behaviour set to a main loop location).
So the main object handling is a call of two different stack, stack “S” has all the object information, stack “U” does the drawing.
Object and behaviour routines are in files starting with the name “objectXXX.asm” (newXXObject, spawnXXXObject, destroyXXX …).
The objects are realized as a linked list (see structure definition). The RAM space for these is allocated in the file “RAMLayoutInGame.asm”. Each type of object has a seperate list.
XXXobject_list (the actual list of continuous RAM structures)
The pointers to the list are defined in “RAMLayoutStatic.asm”, and there are always 3 different entities:
XXXlist_empty_head pointer to first not used object
XXXlist_objects_head pointer to first currently active object
XXXCount count of currently active objects
To each such list also dummy “return” object must be configured. Since the return may change during game stage, these return objects are also located in RAM. They are also defined in above file and are called:
- Opponent shots
- Player shots
- Big ones
Music / Arkos Tracker
The music in Vectorblade is done by vtk using Arkos Tracker Version 1. The playback routines were developed during “Release” and have not changed since. The routines used are for all 3 sound channels.
Amongs others RAM issues do not allow playback during the game.
The routines all reside in bank 1 and are included via file: “arkosPlayerAllChannel.i”.
How these routines are setup and used is described within the routines and within “Release”.
The Arkos tracker routines fill “only” the shadow registers. After all shadow registers are filled, the contents must still be “transfered” to the sound chip.
This can be realized by called the routine (bank1) do_ym_sound2/doymsound100 (“mainBank1.asm”)
Sounds / AYFX
SFX sounds in Vectorblade are realized using the AYFX format (see Vide for documentation).
The routines to play the sounds are the “usual” ones which are included with Vide. No further documentation needed. The above mentioned transfer routines do_ym_sound2/doymsound100 also call all three voices for sfx generation.
VecFever / Flash
Vectorblade supports 2 different media to save game information:
a) Flash chip
Different “compiles” are neccessary, the switch between the two compiles is realised in “commonGround.i” with the definition of the constant “VECFEVER”.
Depending on that constant (0 or 1), a different set of save routines is assembled and some ROM location change (in general Flash saves in bank 0, VecFever saves in bank 1)
Support of VecVox was implemented as an “after thought” and as such can be completely switched off (define constant TEST_VOX). All VecVox routines reside in bank 0 in file “mainBank0.asm” (apart from the needed include files from Alex Herbert).
Level / patterns
The general format and ideas is described on the page: Vectorblade – Level design. The mentioned excel sheet is included in the “other” directory.
The definition files in assembler all start with “f_”:
All of these files are “included” into bank 3.
All code for bankswitching can be found in the file “commonGround.i”.
This file is included first in each bank and the bankswitch routines reside in the “neutral” zone of each bank (start region of each bank is 100% exactly the same).
The RAM in Vectorblade is reused severa times, depending on the stage the game is in.
In general there is a “static” RAM region, which does not change (defined in the file “RAMLayoutStatic.asm”, which defines the first part of the RAM (and also overwrites nearly all BIOS RAM definitions).
The “variable” RAM definitions are stage dependend and are defined in these seperate files:
Sometimes, when only a few stray RAM bytes were needed, I did not create a special RAM file, but created RAM double definitions on the fly – watch out for that in code.
Naming: old bank 0/1 and IRQ0 IRQ1
Since the game started out as a two bank game. I started out naming the banks “bank0” and “bank1”. After I switched to use 4 banks those “old”
bank0 and bank1 in reality should have been renamed to bank2 and bank3.
I did not do that in all cases – just because I was to lazy. Watch out for these irregularily named banks!
The sources of Vectorblade can be compiled with many conditionals (at one stage they all made sense, whether ALL of them still work as intended, I don’t know, I have not tested all of them again.
The conditionals in general are compile-time variables, they are either 0 (not active) or 1 (active). All of the conditionals are defined in the header section of file: “commonGround.i”.
Following defines exist:
(listed in the order of definition within that file)
This defines in general enables a test version of vectorblade. Many of the following conditionals are “wrapped” by TESTING = 1 or TESTING = 0, to provide testing evironment for certain aspects of the game. The “production” version of Vectorblade must have this set to 0!
If defined a time trial mode is enabled. But implementation is not finished, since we decided such a mode is not needed. Since it is not finished the define should always be 0.
Late in development of Vectorblade I implemented the “bonus drops upon death”, to compensate the huge loss of equipment, which follows a death. If enabled the bonus sprawl upon a death, if not – than not.
From a certain difficulty level, the aliens can attack directly during the intro stage. My original plan was, that they start this behaviour with difficulty “normal” – but the beta testers deemed the game to hard. If this conditional is active, the behaviour starts at difficulty “hard”.
This does not do anything.
The original thought behind this was to save RAM space. I wanted to achieve that by shortening the currently used RAM structure of the objects. Currently the behaviour is stored “directly” with a pointer to the behaviour (pointer = 2 byte). If this was implemented “indirectly” thru a jump table, than the offset within a behaviour jump table could be realized with a byte-offset. So each object would be one byte shorter – at the cost of an added indirection.
This was used during “stack debugging” – if active the stack is 16 bytes larger than “normal” at the cost of reducing the maximum active bonus drops from 6 to 4. This is not needed anymore.
If enabled a NMI-handler routine is active which upon triggering shows the registers and stack contents and allows limited RAM browsing.
To have enough space for the NMI handler, some confessions were made:
– enemies have one bullet less
– the (unique) intro music is replaced with the title music (thus one song less)
– the “normaly” within vectorblade used stack must be moved to another address (since it colides with the NMI interrupt vector)
The NMI handler was not tested for quite some time – it should still work though.
If defined, each “game round” the complete RAM contents will be saved to FLASH. During one level all buttons and movements will also be saved to FLASH (till 4096 are full). This is great for debugging! The game will be saved to the “normal” save game location (Bank 0: $a000). The memory $a000 – $afff can be read out from the flash and entered into the source code – thus Vide can “play back” the faulty game – and the location of the bug can easily be examined.
If enabled, you can “load” such a saved game while pressing a button upon startup (beware – due to some logic I did not investigate further, your must press any button during startup, release the button, than press ANOTHER button and keep that one pressed – THAN the playback starts).
Beware, that you do not change the source/binary between recording and playback in any way – because within the saved RAM, there are jump pointers to ROM addresses. If these are not exactly the same, the game will behave differently and most likely crash!
Following changes are made within vectorblade to be able to use this feature:
– no (manual) save game possible
– only possible with FLASH (not VecFever)
– there are only 2 sets of star objects (2 instead of 5)!
– the player has one shot less (9 instead of 10)
– the wheel of fortune does not show – instead the wheel result is randomized
If NMI is enabled, this will add additional information about the current VIA timers to the output.
If enabled (which is the default), the fighter (player) drawing is done in bank 0 (instead of bank3). In bank 0 used to be more memory – so replacing the drawing to bank 0 enabled me to have 6 different fighter drawings (1 for each kind of shot).
I don’t know if this can still be turned off!
During debugging at one stage I suspected, that some crashes were due to “instable” VIA. I still have one vectrex with a partly broken VIA, so I can test this out. If this define is enabled, Vectorblade is a bit slower, but also runs on VIA which have this fault.
This should NOT be enabled. Especially, since I added code later, which again will crash on faulty VIA – so this is not “perfectly implemented” anymore!
This one optimizes (speed wise) colision detection. If a player shot hits a large enemy (money sucker e.g.) than the scoopy shots are NOT additionally tested, but devoured by the large enemy “automatically”!
On very “weird” occasions – if many player shots are on screen, and very few enemies remaining, the colision detection player shot/ enemy can be less than optimal (sometimes a shot goes thru an enemy).
This was rectified by code, which tested ALL shots with ALL enemies from a certain amount of enemies.
This worked great – and no above glitches occured.
But this routine led to other problems, due to the way I implemented the whole collision detection algorythm, which ultimately can result in an endless loop (this was the Big bug, which cost me 3 months to find).
Without changing the entire colision detection system, I was not able to circumvent those possibilities completely.
So – this define should ALWAYS be off, otherwise every couple of hundred levels a crash might occur!
This define belongs to the above conditional and defines from what enemy count the colision detection changes. This is thus not used anymore!
If defined shot 1 to shot 4 are not horizontally aligned but vertically.
The drawing of the shots is quite a lot faster, but IMHO it doesn’t look so nice – so this is usually switched off. I have not tested this option recently – so it might not work anymore correctly.
If defined, the main loop uses the BIOS WaitRecal function instead of my own. This is easier to measure in Vide, especially since this function does not “move” when changing the sources :-).
If set, the stars which move in the background also move in the opposite direction the player is steering.
Enables everything VecVox related.
The level counting goes from 0 – 99. Boss levels are not counted, so e.g. there is level 24, followed by the first boss, followed by level 25.
If this is undefined the level selection (in the settings menu) enables the player to select level 25 – which than results in boss fight 1.
If this conditional is enabled, chosing level 25 results in the level AFTER the boss fight, which really IS the level 25.
If enabled the source is compiled to use the VecFever as a storage device (instead of assuming the game resides on a FLASH prom). Saving/loading of “stuff” is different. Some code also resides at different addresses, since memory locations for saving “buffers” are different.
Following additional defines are possible. All of those are switched to different states depending of the setting of TESTING.
A value from 0 to 102. If an ingame “start level” is set, this value is overwritten!
If enabled, the player can set the difficulty in the settings (regardless of current progress)
Sets the default difficulty that Vectorblade starts with.
(EASY, NORMAL, HARD, IMPOSSIBLE, SUPER_IMPOSSIBLE – and HARDCORE)
Not used anymore!
If defined, destruction of armor renders you for 1 second invulnerable (shield).
If active, than a boss fight does not jump back a random number of levels.
The calibrations values of one of my vectrex is assumed…
If active the title screen (VECTORBLADE + ship) is not displayed during startup.
Value of the bitfield, that defines the shield/armor status:
BITFIELD_ARMOR = $80
BITFIELD_SHIELD = $40
BITFIELD_SCOOP = $20
Startvalue, how many shots are concurrently on screen (Bullet).
Startvalue what kind of shot the player starts with:
1 – single shot
2 – double shot
3 – tripple shot
4 – quad shot
5 – blaster
6 – laser
Start value how fast the player moves.
Start value how fast player shots move.
How much speed is gained by catching one speed bonus (both shot and player).
Start value of how long the timer timeout lasts.
Start value of player money.
Hit points of the big bad alien.
Hit points of the money sucker.
Bitfield of the starting “locks” a player has:
S = scoopy shield,
W = scoopy lock,
LLLL = count of weapon lock
II = 01 immunity against single shots
II = 10 immunity against double and single shots
MMM 1,2,5 multiplyer value
If active the player has unlimited supply of smartbombs.
Only if TESTING is active, start amount of smartbombs.
Only if TESTING is active, start amount of diamonds.
The game starts with a visit to the shop. If active, scoopies (start) are not initialized for testing!
If active the player is allowed to by everything in the shop (he still needs money though).
If active auto fire is ALWAYS active.
If active the mega fiends will occur VERY VERY often.
If active the asteroids in the minestorm are not hurting the player.
If active, the enemies do not shoot!’
If active, the player can not die.
If active, the aliens stay in there waiting positions and do not engange in attacks.
if active, the enemies do not “move” within their waiting positions.
If active, the aliens can not be shot.
If active, the game starts with two attached scoopies. This is not working, when shop is displayed at the start of the game!
If active, the “big bugs” do not move.
Only as test implemented for quad shots, this draw left/right only each alternate frame.
If active, than the scoopies attached to a player can not die.
Random border (out of 255) at what value a warp failure occurs.
Count of lives the player starts with.
Amount of (additonal) power the player starts with.
Bitfield of what secrets are known to the player when the game starts.
%00000001 – SECRET_3_SPEED_SUCKERS
%00000010 – SECRET_RATE_SUCKERS
%00000100 – SECRET_3_BULLET_SUCKERS
%00001000 – SECRET_3_RATE_SUCKERS
%00010000 – SECRET_ORDERED_EXTRA
%00100000 – SECRET_3_FIFTY
%01000000 – SECRET_ORDERED_RANKS
%10000000 – BIGGEST_LOSS_FLAG
Amount of diamonds a player has to collect to afford a “super diamond”
If active the level can be selected in the settings (regardless of the reached level).
if active, every mine storm is a diamond storm.
Main game loop
Following things (in that order) are done in the main “loop”. The main part of the main loop resided in file “mainBank3.asm” and starts at lable: “main12“
- reset of stack
- update global animation counter
- check for “special” huge enemies
(Money sucker …)
- if in intro phase:
do enemy intro
- if not:
check for other big enemies (bugs/saucer/mothership)
- display high score/messages
- display “EXTRA” letters (only the static ones)
- if an enemy spawned a shot -> realy DO spawn the shot
- check for player death -> if so jump to “playerDying“
- draw the player (and shield/scoop/armor)
- jump to bank2 and do the bank 2 part of the main loop (entry at: “gameLoopPart0_m”):
– display all player shots
– display round & 1: read joystick position
– else: read button state -> if button pressed, spawn player shot / jump to pause
– handle vecVox speech
– initialize sounds (shots sound/explosion sound etc)
– display all falling bonus, the bonus display returns to bank 3 (label “bonus_done“)
- reset laser information
- display all stars
within the star the player movement is updated, and the player shots/enemy collision is prepared
- display all enemies
within the enemy display collision detection is realized
- if applicable update laser information from enemy display
- display enemy shots
the shots “return” automatically to the mainloop (label: “main12“)
Vectorblade blog entries
- 10th of march 2018 vectorblade-ii
Excel usage to define levels/patterns
- 18th of March 2018 Vectorblade IV
Bankswitching between 2 banks using PB6
Collision detection, and player shot drawing thoughts
Discusson about enemy mechanics (aliens/bugs/bosses/mega) and their different bahaviour stages
Excursion to behaviour routines, and also description of how to use Vide to realize calls/jumps to other banks
discussion and code about different explosion types
Explanation of different shot types, and description of enemy “scripting” and structures involved
Facts and tryouts of building an own bankswitching pcb 2*48kB
Vectorblade alpha 5 download
Video – 1.5 hours of talk about coding Vectorblade (and Vide)
Some new enemies and behaviours, discussion about PCB design again, and showing the “final” design.
Discussion on how to use IRQ for bankswitching to be able to access 4 different banks.
Description on what types of player shots exists and how they are handled.
First description of different difficulty levels.
Information how Vectorblade is organized as a 4 bankswitch Vide project.
Example on how to use Vide’s scripting ability to automate sending the compiled 256kB binary image via serial transfer to the VecFever.
After many months a non “blabla” Vectorblade blog, this time a summary of all possible costs.
Debug experiences with Vectorblade.
First images of the REAL overlay.
More thoughts on the overlay.
A really nasty bug hunt begins – first usage of a NMI switch to help with the hunt.
Continuation of finding new ways to do bug hunting. Save the game, and save all input – to replay the game and recreate the crash.
The bug was found!