Author - Neil Halliday

S01E05 – Might As Well Jump

Welcome to episode five of our Let’s Remake Manic Miner series. Today we are going to look at making Miner Willy move around the Y axis of the screen. So, in the immortal words of Eddie Van Halen… Might as well jump! Cue cheesy music video, big hair, and flamboyant costumes… Oh, what the hell, here’s a link on YouTube so you can listen while you read!

What is Jumping?

It may seem obvious to you and me, but what actually is jumping? According to Wikipedia…

Jumping or leaping is a form of locomotion or movement in which an organism or non-living (e.g., robotic) mechanical system propels itself through the air along a ballistic trajectory. 

https://en.wikipedia.org/wiki/Jumping

But what does that mean in terms of a game? Well, it’s really dependent on what you are trying to achieve. Looking at the definition above, we can pick out a few words to look at:

Locomotion, movement, propels are all basically the same thing: some form of movement.

Air… hmm, so we know we’re moving through air, but when we’re not jumping, we’re not moving through the air. Does it mean, therefore, that we just stop and float? Of course not; there is that little thing called gravity, that invisible force that causes mutual attraction between all things that have mass. We know about it, but a game doesn’t. But what does gravity do? Simply put, it constantly pulls us down towards the ground until we reach it or land on something that cannot be pulled any further. Like when you are in the upstairs of your house, you are still being pulled towards the ground, but the floor stops you from getting there. Remove the floor, and down you go. If you want to see gravity in action, watch Felix Baumgartner!

A trajectory is essentially a path of movement, and a ballistic trajectory is the motion of an object that is moving in a gravitational field.

Few! We now know, though, that our jump is comprised of a few elements, but we can pick just the two that we need: trajectory and gravity.

Gravity

Gravity is a really easy thing to implement. Effectively, you just pull your character down (increase the Y position) by an ever-increasing amount until you reach terminal velocity. Usually, this is done using a decimal value that steadily increases over time and then gets added to your Y position. To represent this, we need two variables: SPEED and GRAVITY. We constantly increase SPEED by GRAVITY and then add SPEED to our YPOS, thus moving the character down the screen. If we hit the ground or an immovable object of some form, the SPEED variable is reset to zero, ready for the process to start again. A simple jump can then be created by setting the SPEED variable to a negative number to create upward velocity.

You can learn more about jump mechanics in this great YouTube video. It’s not in English, but the subtitles are great, and it does explain jump mechanics very well.

So let’s implement this into our game, shall we? No, we’re not going to do that! It was really interesting to learn how jumping works, but this is not what Manic Miner does. But at least you know how to implement jumping for your other projects!

Manic Miner Jumping

The way Miner Willy jumps is far more prescriptive than the process we described above. When Willy jumps, he travels a predetermined path and keeps going until he hits something or lands on something, in which case he stops. If Willy completes his jump (i.e., his ending vertical position is the same as his starting position), he then falls in a straight line until he hits the ground.

While he is jumping, he cannot change direction, and he cannot jump again. Once you’re jumping, that’s it—you better hope you did it right! Of course, this is one of the skills required to play Manic Miner.

The Jump Parabola

We learned above about the ballistic trajectory, but how would we go about creating something like that without the technique described at the beginning of this episode? The answer is quite simple and quite cheaty, but because of the movement of Miner Willy, it works perfectly for us. We will use a sine wave. Not all of a sine wave, just 180 degrees of a sine wave, which creates an appropriate parabola to work with. To make things easier, though, we will define a full 360-degree sine wave. Let’s add the following code:

@InitMinerWilly
    dim mwan(319) : for xpos=0 to 319 : mwan(xpos) = 1 + ((xpos mod 8)/2) : next xpos
    dim jmps(359) : for ang = 0 to 359 : jmps(ang) = -(sin(rad(ang))*20) : next ang
    MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80
    gosub @SetOFS
    return

There is not much further to explain here, as we have been through for/next loops and arrays, but what about the sin and rad commands?

CommandDescription
SinCalculates the sin of an angle, returning a floating point number as a result.
RadConverts angles expressed in degrees into radians. A radian is approximately 57 degrees.

Now we need to make sure that we adjust Miner Willy’s Y position on the screen by the jump. But, before we can do that, we need to be able to identify the state that Willy is in (hopefully not the state that we find our hero in at the beginning of Jet Set Willy!). Let’s talk a little bit about finite state machines.

What is a Finite State Machine?

This will likely be a new concept for a lot of people, but it’s really very simple. It is a mechanism for allowing something to be in exactly one of an infinite number of different states at any given time. This model can be used for many different types of applications, but it is massively used within gaming, particularly in instance-based languages such as Games Maker Studio from YoYo. They are used, amongst many other things, for indicating the state of a character—what it is currently doing.

Examples of this could be idle, walking, shooting, flying, or, in our case, jumping.

So what we can do is create a series of variables to capture Willy’s state.

@InitMinerWilly
    FALLING = 1 : JUMPING = 2
    dim mwan(319) : for xpos=0 to 319 : mwan(xpos) = 1 + ((xpos mod 8)/2) : next xpos
    dim jmps(359) : for ang = 0 to 359 : jmps(ang) = -(sin(rad(ang))*20) : next ang
    MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80 : MWSTATE = FALLING : MWJMPANG = 0 : MWVSPD = 0
    gosub @SetOFS
    return

We’ve defined two variables, FALLING and JUMPING, which have different values. We’ve then created a new Miner Willy variable, MWSTATE (Miner Willy State), so we can determine what our hero is currently doing. By default, because of the effect of gravity, we say that the default state of Miner Willy is falling.

Finally, we’ve created MWJMPANG (Miner Willy Jump Angle) so we know at what position in the jmps Miner Willy is currently in, and finally MWVSPD (Miner Willy Vertical Speed) so we can ultimately make Willy drop to the floor (this is simulating our gravity, if you like). We’ll use this later on, but it’s better to get the definition out of the way.

We’ve got our different states, and we’ve assigned the default to Willy. Our next task is to actually make Willy jump. This happens when the fire button on the joystick is pressed, so we need to make a small amendment to our joystick routine, but there is also some setup that we need to do, such as signaling that Willy is jumping, etc. Let’s create a new subroutine first.

@StartJump
    MWSTATE = JUMPING : MWJMPANG = 2 : MWVSPD = 0
    return

As you can see, we are setting MWSTATE to be JUMPING, setting our current jump angle (MWJMPANG) to be 2, and resetting the vertical speed (MWVSPD) so that we are not moving vertically – We’ll explain that later on!

Let’s read the joystick fire button and call the @StartJump subroutine.

@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    if JF then gosub @StartJump
    if not(JSTCK) then MWSPD = 0 : else MWSPD = MWWS : if JL then MWD = -1 : gosub @SetOFS : else MWD = 0 : gosub @SetOFS
    return

Nothing is going to happen if we run our routine now, because we are not using any of the new variables we have just created. So let’s affect Miner Willy with our jump logic.

@DisplayMinerWilly
    if MWSTATE = FALLING then MWVSPD = 1 : else MWVSPD = 0 : MWJMPANG = MWJMPANG + 2 : MWSTATE = JUMPING : if MWJMPANG > 180 then gosub @ResetJump
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,MWYPOS,mwan(MWXPOS) + MWOFS
    return

We’re saying here that if Miner Willy is FALLING, then his vertical speed (MWVSPD) is 1, so as to move him down the screen. else, we set his vertical speed to 0, increase the jump angle by 2, and set his state to JUMPING. We then check MWJMPANG and if it has reached 180 degrees, we call the @ResetJump subroutine to stop Willy from jumping.

The @ResetJump subroutine looks like the following:

@ResetJump
    MWSTATE = FALLING : MWJMPANG = 0
    return

In this routine, we simply reset Miner Willy’s state to FALLING and clear the MWJMPANG.

Finally, we can use all of the above to affect the Y position of our hero:

@DisplayMinerWilly
    if MWSTATE = FALLING then MWVSPD = 1 : A = MWYPOS : else MWVSPD = 0 : A = MWYPOS + jms(MWJMPANG) : MWJMPANG = MWJMPANG + 2 : MWSTATE = JUMPING : if MWJMPANG > 180 then gosub @ResetJump
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,A,mwan(MWXPOS) + MWOFS
    return

So, you can give that a run, and you will see that Miner Willy now jumps when we press the fire button. BUT… and it’s a big but… You can press fire again while Willy is jumping, and it all goes wonky. Not only that, but you can change directions! Argh! Don’t panic; it’s easily solved. We can just use our state engine on the joystick input routine and bypass some stuff—whoop!

@ReadJoystick
    if MWSTATE = JUMPING then goto @EndReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    if JF then gosub @StartJump
    if not(JSTCK) then MWSPD = 0 : else MWSPD = MWWS : if JL then MWD = -1 : gosub @SetOFS : else MWD = 0 : gosub @SetOFS
@EndReadJoystick
    return

So, what we are doing here is essentially saying: If Miner Willy is jumping, then I don’t want to read the joystick any more and change our speed or jumping variables, so just goto the @EndReadJoystick label instead. Once you’ve jumped, you’d better hope that it was the right jump to make.

Let’s Apply Gravity

We’ve not done anything with our gravity as of yet, so we can do that now. Remember, gravity continuously pulls an object down, so it’s really very simple to implement. Let’s create a new subroutine to apply gravity to our MWYPOS.

@ApplyGravity
    MWYPOS = MWYPOS + MWVSPD
    if MWYPOS > 80 then MWYPOS = 80
    return

Literally, all we are doing here is adding Miner Willy’s vertical speed to his Y position. By doing it this way, we can simply change the value of MWVSPD to 0 when he hits something. I’ve put a quick if statement in there on the MWYPOS variable just so that we can stop Willy from falling off the bottom of the screen. We’ll remove that once we get a background and some collision detection in place.

All that is left is to apply the gravity:

@DisplayMinerWilly
    gosub @ApplyGravity
    if MWSTATE = FALLING then MWVSPD = 1 : A = MWYPOS : else MWVSPD = 0 : A = MWYPOS + jmps(MWJMPANG) : MWJMPANG = MWJMPANG + 2 : MWSTATE = JUMPING : if MWJMPANG > 180 then gosub @ResetJump
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,A,mwan(MWXPOS) + MWOFS
    return

And there we go! Miner Willy walks and jumps now! Yippee.

And so, we’ve reached the end of another exciting episode of Let’s Remake Manic Miner. As always, the source files can be found in the GitHub repository.

In our next episode, we will start to look at how to build an environment for Miner Willy to walk around in. Until then… Happy STOSing.

S01E04 – Animating Willy

Welcome to episode four of our Let’s Remake Manic Miner series. Today we are going to look at animating Miner Willy as he moves around the screen.

How We Normally Do It

Animating characters is usually straight-forward; in fact, you could say it’s probably one of the easiest things you can do with your sprite. Effectively, you just need to cycle around a series of images while your character is moving, and then when the character stops, you stop cycling around the image. If the character changes direction, you reset the animation frame for the start animation of the new direction.

So, let’s do that, shall we? No, we won’t do that, because that is not how Manic Miner works, it does something that I never really thought of before, but it’s quite a clever trick that Matthew Smith did, and it saves a bunch of processing time too! Let’s ask ourselves…

What Would Matthew Smith Do?

I’ve spent long hours looking at how Miner Willy is animated, and it had me puzzled for a while. I tried various different things, but I could just not get it to look right. After a bit of a break and doing some other things and thinking, my mind came to a simple conclusion: Willy’s animation frame is tied to his X position on the screen. This is why he keeps animating when jumping, and also why, no matter how you’ve moved about on the screen previously, you always end up with the same frame of animation when Miner Willy is in the same position.

You can try this out for yourself by playing the Central Cavern level and walking left to the brick wall. Miner Willy will stop mid-walk and not animate any further. Move around the screen, jump, do random things—when you walk back to the left, the frame of animation is always the same! Try some other things too, such as walking off the first platform. Miner Willy will always be in exactly the same frame of animation. If we were to use the standard method as described above, this would most certainly not be the case. Therefore, his animation frame is tied to his X position!

Now, this sounds like a bit of a cop-out, but in fact, it’s absolute genius! If you base the character’s animation frame on their current X position, there are no complex animation calculations such as counting frames or resetting when you change direction—none of that! Therefore, you are saving lots of CPU instructions and making your game run faster, which, when working with something like the ZX Spectrum, the more you can squeeze out of the CPU, the better. So, when I say genius, that’s exactly what it is.

How Do We Do This In STOS?

So, how do we replicate this within STOS?  Well, it’s really pretty simple really, we just define an array that holds the correct animation frame to show at the different horizontal positions on the screen. We know that our Miner Willy sprite has just four frames of animation, and we know that sprite #1 is walking to the left. Therefore our Miner Willy sprites are as follows:

Frame NumberDescription
1Miner Willy walking left – Frame 1
2Miner Willy walking left – Frame 2
3Miner Willy walking left – Frame 3
4Miner Willy walking left – Frame 4
5Miner Willy walking right – Frame 1
6Miner Willy walking right – Frame 2
7Miner Willy walking right – Frame 3
8Miner Willy walking right – Frame 4

So, what we can do is predefine which sprites to use and store them in an array so that we don’t have to be calculating which frames to be using all the time. We can then use the direction variable (MWD) to adjust the frame accordingly by an offset. The less calculations we have to perform per frame, the quicker our game logic will run.

Let’s modify our initialisation routine so that we precalculate the frames that represent each X position on the screen.

@InitMinerWilly
    dim mwan(319) : for xpos=0 to 319 : mwan(xpos) = 1 + ((xpos mod 8)/2) : next xpos
    MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80
    return

We’ve introduced some new commands there, so let’s walk through them and determine what they are doing.

CommandDescription
dim mwan(319)Here we are setting up array MWAN (Miner Willy ANimation) and we are telling it that we want enough storage to hold 320 values. When defining arrays in STOS the first value is always 0, so in the instruction here we are defining values 0 to 319, so, 320 spaces.

But what is an array? Think of an array as a list of values that can be accessed by an index number. In our case we have said we want 320 indexed values to be stored. If we tried to store any more, STOS would generate an error message as in affect, we would be overwriting other memory.

Arrays can be single or multi-dimensional, but we won’t get into that just yet. Our example here is a single dimensional array.
for xpos=0 to 319Here we are starting a for/next loop that is going to generate values between 0 and 319 within the XPOS variable. This value represents the number of pixels across the screen.
mwan(xpos) =We are addressing our array here, and passing the XPOS variable as the value index, this accessing indexes 0 to 319.
1 + ((xpos mod 8)/2)We’re doing a little bit of mathematics here on on our XPOS value, and introduced the MOD command. MOD returns the modulus of a value, that is to say it’s used to divide two numbers and only return the remainder. In this instance we are dividing XPOS by 8 and returning the remainder.

But why are we doing this? Well, the XPOS value as we know will always be increasing in value from 0 to 319. By using the MOD command, we can ensure that we always get a calculated value of between 0 and 7, no matter what the value of XPOS is.

We then divide that value by 2, which results in a value of 0 to 3.

We add 1 to that result so we end up with a value of 1 to 4 – or an animation number – whoop!
next xposHere we are simply finishing off the for/next loop. All instructions between the for and the next will be executed the number of times specified in the for command.

Our MWAN array now contains the appropriate animation frame to use based on the X position on the screen. We now need to update our sprite command to make sure we are using the appropriate frame.

@DisplayMinerWilly
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,MWYPOS,mwan(MWXPOS)
    return

Let’s build and run our project to see what we have. If you move your joystick left/right now, you should see Miner Willy walk – look at him go! But, one major draw back, he’s always facing to the left. That’s because our frames of animation we have calculated only register frames 1 to 4. We need a way to make him face to the right. Remember that MWD variable we created previously? That’s where this comes in, because we can now use that to create an offset to point to the correct animation frame.

We could do some complex mathematics here to transform the frame number to the right place, but complex maths means more processor time, so let’s just keep it simple and quick instead. We will create an offset flag that holds a number that needs to be added to the frame. If Willy is walking to the left, then the offset will be 0. If Willy is walking to the right, then the offset will be 4. When we add this offset to the animation frame number, Willy will be facing in the correct direction. Ok, so let’s make a few code changes:

Firstly, we create a new subroutine called @SetOFS, this is what we will use to set what our frame offset should be, using the MWOFS (Miner Willy OFSet) variable.

@SetOFS
    if MWD = 0 then MWOFS = 4 : else MWOFS = 0
    return

next, we need to call this new subroutine from a couple of places.

@InitMinerWilly
    dim mwan(319) : for xpos=0 to 319 : mwan(xpos) = 1 + ((xpos mod 8)/2) : next xpos
    MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80
    gosub @SetOFS
    return
@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    if not(JSTCK) then MWSPD = 0 : else MWSPD = MWWS : if JL then MWD = -1 : gosub @SetOFS : else MWD = 0 : gosub @SetOFS
    return

As you can see on the second section of code, we are calculating the new offset when MWD changes.

And finally, we now need to utilise the offset on the Miner Willy sprite.

@DisplayMinerWilly
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,MWYPOS,mwan(MWXPOS) + MWOFS
    return

Let’s build our project and see what we have. Success! Miner Willy is walking in the direction he is facing. Well, he’s running actually, so we will need to slow that down a little bit, but we can tackle that later.

For now, that’s the end of this episode. Check in for the next episode where we will be making Willy jump. Remember, you can find the various episode source codes and assets on the Manic Miner Github page.

S01E03 – Making Willy Walk

Displaying Miner Willy

So, we want to make Miner Willy move then? Well, we can’t do that unless he’s shown on the screen. In our previous episode, we created our game loop, and so we need to make an adjustment to that loop in order to call our display routine for Miner Willy.

repeat
    gosub @DisplayMinerWilly
    gosub @WaitVBL
until false

For our subroutine, we will keep it simple for the moment and just place Miner Willy at a specific location on our screen.

@DisplayMinerWilly
    sprite 1,100,100,1
    return

We have already learned about the label and the return command in previous episodes, but what else are we doing here? We’ve introduced a new command:

CommandDescription
sprite 1,100,100,1This is the official definition of the sprite command as written in the STOS manual:

SPRITE n,x,y,p
Displays sprite number "n" on the screen at coordinates "x" and "y".

"n" is the number of the sprite, which can range from 1 to 15. It is this number that will be used to identify the sprite in any subsequent calls to the MOVE and the ANIM instructions.

"x" and "y" are the coordinates of the point on the screen where the sprite is to be drawn. Unlike normal screen coordinates, these can take NEGATIVE values. The "x" coordinate can vary from -640 to +1280, and the "y" coordinate from -400 to +800. This allows you to move a sprite off the screen without causing an error.

"p" specifies which of the images in bank 1 is to be used for a particular sprite. The only limit to the number of these images is the amount of available memory.

Each sprite has an invisible handle through which it can be manipulated, called a "Hot Spot". Whenever we draw a sprite, we always specify its coordinates in terms of the position of this point on the screen. As a default, the hot spot is always set to the top left-hand corner of the image, but this can readily be changed using a special option from the sprite definer accessory.


Phew! There is quite a bit to take in there, but essentially what it is saying is that we have a total of 15 sprites that STOS can deal with. When we create a sprite, we have to tell STOS which number of the 15 sprites we are creating: “n”, as it calls it in the manual. We have assigned sprite number 1 to Miner Willy.

Next, we are specifying where to show Miner Willy on the screen—his x and y coordinates. We have just chosen to show Miner Willy at 100,100 on the screen.

Finally, we specify which of the images we want to show—in other words, which of the image numbers from the sprite bank we have loaded. We are using 1 here, which is Miner Willy facing left.

Let’s transpile our program, load it into STOS, and run it. We should simply get Miner Willy, frame 1, at position 100,100 on the screen. It works! But now we need to make him move.

A Moving Willy

We need to define some form of mechanism to make Miner Willy move. In the ZX Spectrum version of Manic Miner, this was done purely with keyboard input. We could do the same for our game, but for ease, I’ve decided to use the joystick to control our hero. We therefore need to read some input from the joystick, which STOS has some handy routines to deal with.

Before we can do anything in terms of joystick input, we need to do some setup so we can control the position of Miner Willy. We always need to know his current x and y positions on the screen so that we can increment them as required for walking and jumping. We need to do this in a series of variables, which we will need to initialise before trying to display Miner Willy. There’s also some additional setup that we will do too but won’t use for the moment; this is to cater for the fact that we are not going to use either the sprite movement or animation routines that are built into STOS.

Let’s create a new subroutine for initialising everything we need for Willy.

@InitMinerWilly
        MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80 : MWSPD = 0

There are a fair few variables there! I’ve tried to use logical naming conventions for them, but let’s go through them one by one and describe what they are used for:

VariableDescription
MWDMiner Willy Direction

Will be used to identify the direction that Miner Willy is currently facing. MWD with a value of 0 will mean that Willy is facing to the right, and -1 will mean he is facing to the left. We use 0 and -1 for a reason, which will become clear later on.
MWWSMiner Willy Walk Speed

This value will be used to identify the number of pixels that Miner Willy will move with each frame of animation when walking.
MWWCMiner Willy Walk Counter

This value is an interesting one. Miner Willy is only a small chap and doesn’t walk very fast. If we were to move and animate him a single pixel every 50th of a second, he would be running the 100-metre dash across the screen. So we need to slow him down a little bit. This is where the walk-counter comes in. Let me explain a bit:

When the joystick is pushed to the right, we move Willy to the right by the number of pixels as defined by MWWS and change his animation frame. If this is happening at 50 frames per second, then it would take 6 seconds for Willy to cross the entire screen, and the animation frames 1-4 would happen 12 times in just a single second—it would be super fast.

Therefore, to slow him down, we need to introduce a counter. We can use this to control the speed by decrementing the counter and then only moving and animating when it reaches zero. At which point we reset the counter back to its maximum value. MWWC holds the default value of the counter that can be used to change the speed and reset our main counter (see below).
MWWCCMiner Willy Walk Counter Current

This is the current value of the walk counter. This value is decreased at each VBL, and when it reaches zero, we will update Miner Willy’s position and current animation frame. We then reset this value back to the default setting (MWWC) for it to happen all over again.
MWXPOSMiner Willy X POSition

Holds Miner Willy’s current X position on the screen. It is this value that is increased to move him to the right and decreased to move him to the left. We’re giving him a default X position of 16 to start with.
MWYPOSMiner Willy Y POSition

Holds Miner Willy’s current Y position on the screen. It is this value that is increased to move him down the screen and decreased to move him up. We’re giving him a default Y position of 80 to start with.
MWSPDMiner Willy SPeeD

We will use this variable to hold the current speed that Miner Willy is moving horizontally. More about this later.

We now need to make a call to our initialisation routine and also update our sprite command so that we are not forcing Willy to always be at position 100,100 on the screen. So we do that after we have loaded our assets. We may need to move this later on, but it serves it’s purpose here for the moment.

gosub @LoadAssets
gosub @InitMinerWilly

Now, we need to update our sprite command so that we are using the newly created MWXPOS and MWYPOS variables.

@DisplayMinerWilly
    sprite 1,MWXPOS,MWYPOS,1
    return

OK, let’s transpile our program, load it into STOS, and see what we have. You should now see that Miner Willy has moved positions. He’s further to the left and higher up the screen. That means that our sprite positioning with MWXPOS and MWYPOS is working, and we can begin to manipulate those with the joystick input.

Joystick Input

To get Miner Willy to move, we need to read the joystick data stream. STOS provides joystick input commands, so we will use them. Let’s go ahead and create a new function that we can call whenever we want to read the joystick.

@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    return

Let’s breakdown the calls that are being used:

CommandDescription
JL = jleftHere we are reading the value of the jleft command and storing it in our variable JL. If the player is pushing the joystick left, then JL will be TRUE, otherwise, it will be FALSE.
JR = jrightAs with the jleft command, we are reading if the player is pushing the joystick to the right. Again, TRUE or FALSE will be returned into JR.
JF = fireYou guessed it, we’re reading the status of the fire button. JF will be TRUE if fire is being pressed, and FALSE if not. We are going to use fire to ultimately make Miner Willy perform a jump.
JSTCK = (JL or JR)This is an interesting one. What we are doing here is creating a third variable that identifies if the joystick is being pushed either left (JL) or right (JR). The variable JSTCK will therefore contain TRUE if we are pushing the joystick in any horizontal direction. With this variable set, we can then start to perform operations that should only be performed when Miner Willy is moving.

The above is simple enough, but why are we storing the state of the joysticks in variables? Well, we do this for a couple of reasons. Essentially, the input from the joystick is a continual stream of data being thrown at STOS. If we used the direct joystick commands throughout our source code, we could end up with some strange results as the joystick states may be different whilst our code is executing. For example, making Miner Willy do something at a point in which he shouldn’t be doing it. In addition to this, we would force STOS to perform additional reads and therefore consume more processor time at each call. By storing the values within variables, we only read the joystick once per VBL and therefore save a bunch of processing power.

Now we need to make sure that we call the routine to get the values from the joystick. We must do this within the game loop, so add the following code:

repeat 
    gosub @ReadJoystick 
    gosub @DisplayMinerWilly 
    gosub @WaitVBL 
until false

Applying Movement to Willy

We’ve got Miner Willy all set up now, and we have our variables available for changing his X and Y positions on the screen. We’ve also started to feed joystick input into our JL, JR and JF variables. Now that we’ve got that in place, we need to make Miner Willy move. Earlier, we created the MWSPD variable to hold the current speed at which Miner Willy is walking. It is this variable that we manipulate in order to signal that movement is to occur. We do this within the @ReadJoystick section of code.

@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    if not(JSTCK) then MWSPD = 0 : else MWSPD = MWWS : if JL then MWD = -1 : else MWD = 0
    return

Let’s break down that new line of code that we have just added in:

CommandDescription
if not(JSTCK)What we are doing here is testing that the JSTCK variable is set to FALSE. This could be done in a couple of ways. I have chosen to use “not”, which inverses the content of the variable. If you remember earlier in the series we spoke about how all conditions return TRUE or FALSE. Well, that what this does, if the value of JSTCK is FALSE the “not” command inverts it and therefore the tested condition is TRUE, and vice-versa if JSTCK was TRUE.

We could also have done if JSTCK = false and again, that creates a condition that returns TRUE or FALSE.

Hope that makes sense!
then MWSPD = 0If JSTCK is FALSE, then we set the current speed of Miner Willy to zero. There is no input on the joystick, and therefore Miner Willy is not moving and his movement speed is zero.
else The else clause of the if statement tells us what to do when the if condition does not equate to TRUE. Here we are saying, if JSTCK is TRUE (i.e. we have joystick input), then we need to execute the following code.
MWSPD = MWWSWe set the current walking speed of Miner Willy to the default walking speed that has been defined.
if JL then MWD = -1Is the joystick being pushed to the left? If so, then set the walking direction (MWD) to be -1.
else MWD = 0Else, the joystick must be pushed to the right, so set the walking direction to 0. We can make this assumption here as the initial check on the JSTCK variable told us that either left or right was being pushed. Therefore, if it’s not left (JL) as per the if check, then it can’t be anything but being pushed to the right.

We now know in which direction Miner Willy is facing (MWD) and at what speed he is moving (MWSPD). This information can now be used to update the MWXPOS variable accordingly. We’ll add this code into the @DisplayMinerWilly section.

@DisplayMinerWilly
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,MWYPOS,1
    return
CommandDescription
if MWD = 0 thenFirstly, we check the direction that Miner Willy is facing, remember, 0 is to the right and -1 is to the left.
MWXPOS = MWXPOS + MWSPDMWD does equal zero, so add the current speed to Miner Willy’s X position. In other words, increase the X position, thus moving him to the right.
else MWXPOS = MWXPOS – MWSPDMWD does not equal zero, so subtract the current speed from Miner Willy’s X position, thus moving him to the left.

Transpile the code and see what happens. You should have a Miner Willy that now moves left and right on the screen using the joystick.

That’s all for this episode, folks. In the next episode, we will look at animating Miner Willy using a very interesting technique!

In the meantime, you can find the various episode source codes and assets on the Manic Miner Github page.

GBP STOS Basic & Compiler Extension Manual

Introduction

Welcome to the STORM Developments/GBP Software STOS basic and compiler extensions. Many thanks for your interest in this program, and thanks also for your registration fee. Your details have been placed in our registration database, allowing us to keep track of registered users and also send free program updates to those who want them.

We are sure that you will enjoy using the compiler version of the extension, and also hope that your programs will now be enhanced because of the use of GBP.

We would like to hear from you in the future to keep in touch with how you are getting on, solve any problems that you might have, or listen to any ideas that you may have about future extensions. Please do not hesitate to get in contact with any of the GBP team.

Thanks

Before we begin, I and the rest of the GBP team would like to thank a couple of people and publishers who were a great help during the writing of this extension.

Billy Allen: Thanks for Misty & The Missing Link, it really inspired us to get GBP finished. Many thanks for the letters, and I hope that I shall hear from you quite soon.

Steve Jarrett: Thanks for all the comments about the extension.

Rick Dunlop: Again, thanks for taking the extension in, and I hope you didn’t mind me sending you a new version every week. How are you going with the PC? What’s up?? Are your routines not fast enough to work on an ST????? (joke.. I think!)

Stephen Hill: Thanks for creating such a wonderful book called “The Game Maker’s Manual”. It was really useful while I was originally coding and finally debugging the extension. I hope you write a follow-up.

Sigma Press: Thanks for publishing Stephen’s book!

Abacus Software: Thanks for publishing the “Atari ST Internals” book. It should play a major part in any assembler or ST programmers book collection!

Disclaimer

Although this extension has been tested to it’s full abilities, GBP Software or any member of GBP Software cannot be held responsible for anything that may happen to computer equipment of any kind during the use of the extension. Basically, you’re on your own if it does go wrong!

Copyright

These extensions and accompanying documentation are the SOLE copyrights of GBP Software, and may not be ripped off, copied, sold without our prior knowledge, or be changed in any way, including the documentation. It is an offense to copy the compiler extension without written permission from GBP Software. The BASIC extension and the DOCUMENTATION are shareware and can be copied freely. The Shareware release may be used for a 30-day period before registration becomes necessary.

Remember, copying copyrighted material is a criminal offense. Please register this product to make sure that GBP Software will continue to produce great-quality STOS extensions in the future.

Thank you for your attention.
Neil Halliday.

The Commands

Well, here’s the bit you have all been waiting for: the commands! I must apologise at this point, as all the commands are not in any sort of logical order, but I will try to keep them together. I will also try to include demo programs where applicable.

NOTE: All addresses that are mentioned in this extension must be passed as actual addresses, as the routine will not work by simply passing the bank number. i.e., the extension will bugger up if you use…

            Eplay 10,length(10),0,0,1

As the routine will play from memory address 10 instead of bank 10. So you will have to call it like this…

            Eplay start(10),length(10),0,0,1

Ok??? For more information about that, consult your STOS manual.

MISCELLANEOUS COMMANDS

LIGHTS ON

Syntax: Lights On

Description: This is a very nice command that will turn on the drive lights for disk drives A & B. This command will allow you to produce effects that have been seen in demo screens where the drive lights pulse to a piece of music and has also been seen recently at the beginning of Crack Art.

LIGHTS OFF

Syntax: LIGHTS OFF

Description: The opposite of the last command. You have read the last command, haven’t you? As mentioned about pulsing the drive lights to music, here is the routine:

10 dreg(0)=1 : call 10 : rem * Call music Init
20 loke $4d2,start(10)+8 : rem * Install on VBL
30 if psg(8)>12 then lights on : else lights off
40 wait vbl : goto 30
50 :
60 loke $4d2,0 : bell : rem * Turn music off

The above routine will work with a MAD MAX music file loaded into bank 10, but if you don’t have any MAD MAX music you can use the normal STOS music (VERY CRAP!) by making these amendments:

10 rem * Routine to flash lights to CRAP MUSIC!
20 music 1

For more information about the PSG command, turn to your manual pages 265 & 266 (Ha! That will fool all you people who are using a copy of STOS!!! Buy the original, you SAD people!).

PREADY

Syntax: X=PREADY

Description: This function tells you if the printer connected to the Parallel port is online or not. The variable X will contain the status of the printer, and can only take 2 forms.. TRUE (-1) if the printer is on line and ready to receive data, or FALSE (0) if the printer is set to anything else. Try the following small program to test this out:

10 X=pready
20 if X then bell
30 goto 10

This program will test the printer, and if it is online, it will sound the STOS bell repeatedly; otherwise, if your printer is offline, nothing will be heard. For more information about true and false statements, consult your STOS manual, page 227.

EVEN

Syntax: X=EVEN(NUM)

Description: The even command will return if the input value is even or odd. The output variable X will contain true if NUM was even or FALSE if NUM was odd:

10 input A
20 if even(A) then bell : else boom

SETPRT

Syntax: X=SETPRT(VAR)

Description: This function allows the printer configuration to be set. The variable VAR is a bit vector with the following meaning:

BitnumberOff (0)On (1)
0Dot MatrixDaisy Wheel
1MonochromeColour
2Atari PrinterEpson or Compatible
3Test Mode (DRAFT)Print Mode (NLQ/LQ)
4Centronics PortRS-232 Port
5Continuous SheetSingle Sheet
6-14ReservedReserved
15Always 0Always 0

As an example, the following statement would set the printer to:

  • Dot Matrix Type
  • Monochrome
  • Epson or Compatible
  • Test Mode (DRAFT)
  • Centronics Port
  • Continuous Sheet
X=setprt(%000100)

The bit number, as you will have noticed, is binary, with bit 1 being on the far right. This binary number can be substituted for the decimal equivalent, so the command:

X=setprt(4)

Is equivalent to:

X=setprt(%000100)

The printer’s parameters can be read by passing -1 as the input variable. The value will be returned as X.

SPECIAL KEY

Syntax: X=SPECIAL KEY(I)

Description: The special key command sets or returns the current status of the special keys (i.e., Shift(s), Alt, Ctrl, and Caps). The status can be set by passing variable I as a positive integer, and by passing I as -1, the status can be read.

10 print "Current Status :"; special key(-1)

The value returned by special key is an 8 bit value that is read in the following way:

BitMeaning
0Right Shift Key
1Left Shift Key
2Control (CTRL) Key
3Alternate (ALT) Key
4Caps lock
5Right Mouse Button (CLR/HOME)
6Left Mouse Button (INSERT)
7unused

If the bit is set, then the button is active.

HCOPY

Syntax: HCOPY X

Description: The annoying thing about STOS is the fact that no matter how good your games, demos etc.. are, they can always be interrupted by the system HARDCOPY command (ALT & HELP). Well, that is no longer the case. This command will turn off and turn on the system Hardcopy! By passing X as 1, we turn ALT & HELP on, and by passing X as 0, we turn ALT & HELP off!

10 hcopy 0 : rem Turn HARDCOPY off
20 wait key
30 hcopy 1 : rem Turn HARDCOPY on

File Commands

D CRUNCH

Syntax: D CRUNCH ADDR

Description: The D CRUNCH command allows you to unpack files compacted with the most popular packers around (up to now). By passing ADDR as the address of your compressed data, it will be decompressed by the recognised file format. The extension can unpack several different formats, which will be shown in a little while, but before we do, a little word of warning! The de-pack routines are a0 -> a0 routines, which means that any compressed data will be overwritten during decompression. It also means that memory banks will have to be reserved for the original file length of the data; otherwise, you face overwriting some data that you may need and, in some cases, even crashing your ST.

PAKTYPE

Syntax: X=PAKTYPE(ADDR)

Description: All recognised packer formats have a special header to tell them from one another.. This command will return in variable X, what packer the data at address ADDR was compressed with. If the packer type is not recognised then the return value will be 0. Otherwise the value stands for:

ValuePacker
1Speed Packer 2
2Atomik v2.5
3Ice v2.11
4Automation v5
5Ice v2.40
6Fire v2.0
7Speed Packer 3

PAKSIZE

Syntax: X=PAKSIZE(ADDR)

Description: This command returns the uncompressed size of the packed file located at address ADDR.

FSTART

Syntax: X=FSTART(N,ADDR)

Description: This command is used in conjunction with a GBP file bank, and returns the position in memory of file N, located in the GBP filebank that can be found at address ADDR. A GBP bank allows many files to be stored in just one memory bank, which programmers will understand that means loads of data loaded, and still loads of banks left.

FLENGTH

Syntax: X=FLENGTH(N,ADDR)

Description: This command will return the length of file N, found in the GBP bank located at address ADDR.

FOFFSET

Syntax: X=FOFFSET(N,ADDR)

Description: This command returns the offset of the file N, compared with the start of the GBP file bank. i.e file two may by 1024 bytes from the start of the bank. NOTE: The GBP bank builder accessory can be found along with the extension files.

Graphics Commands

FASTWIPE

Syntax: FASTWIPE ADDR

Description: This is a very fast version of the STOS cls command. It will clear 32000 bytes from the address specified by ADDR. It can be used to clear memory banks or screen addresses (eg. physic). However if you are clearing a memory bank you must pass the parameter as the start of the bank. e.g..

10 fastwipe start(BNK)

Where bank BNK is a reserved screen bank, if you just want to clear a screen address then substitute start(BNK) for the address of the screen. e.g..

10 fastwipe physic

You may have seen the wipe command in the The Missing Link extension? Well, no we haven’t ripped/stolen the routine, it was mine in the first place and I sent it to the chaps, I never got credited for it (snarl), anyway.. This new version is even faster (Shove that up your jumper Top-Notch! haw haw!).

ELITE UNPACK

Syntax: ELITE UNPACK ADDR1,ADDR2

Description: This command will allow you to unpack a Degas Elite compressed PC? pictures. You must set up a normal STOS screen bank to hold the resulting unpacked picture.This routine SHOULD work in all three resolutions, but I haven’t tested it yet. Try this small routine:

10 key off : curs off : mode 0 : hide
20 reserve as screen 10
30 reserve as work 11,(length of PC? file)
40 bload "FILENAME.PC?",11
50 elite unpack start(11),start(10)
60 screen copy 10 to physic
70 get palette(10)

This will unpack the picture from bank 11 to bank 10, and then display it on the screen. Once the picture has been unpacked, the palette can then be grabbed in the normal way. Screens can also be unpacked directly to the screen, i.e.

elite unpack start(11),physic

The palette for the picture can then be installed by using the command:

get palette(physic)

TINY UNPACK

Syntax: TINY UNPACK ADDR,ADDR2

Description: This command does exactly the same as the ELITE UNPACK command, except that it will unpack a TINY compressed image.

CA UNPACK

Syntax: CA UNPACK ADDR,ADDR2

Description: Again this command is the same as ELITE UNPACK & TINY UNPACK, although this routine will packed an image file saved in the Crack Art format (CA?).

CA PACK

Syntax: X=CA PACK ADDR,ADDR2,PAL,MODE
Description: This command is to be used in conjunction with the CA UNPACK command, and will actually create a compressed Crack Art image file from a standard STOS screen bank. ADDR is the source image, ADDR2 is the destination for the compressed image, PAL is the address of the palette data to be used, which will usually be ADDR+32000, and MODE is the pictures screen resolution (0=Low, 1=Medium, 2=High). The value returned is the length of the compressed picture file. This routine should help you sort this out:

10 reserve as screen 10
20 L=ca pack physic,start(10),physic+32000,0
30 bsave "picture.ca1",start(10) to start(10)+L

This routine will compress the current physical screen into bank 10, and then save it. The variable L contains the compressed length of the image file.

SETPAL

Syntax: SETPAL ADDR
Description: The SETPAL command will install a new palette that is located at address ADDR. It is useful for storing large palette changes in a memory bank, and then setting them when every they are needed. The format for the data is just the standard degas format, in that it is 16 words, each word representing the colour 0 – 15.

10 reserve as work 10,32 : mem=start(10)
20 restore 80 : for lp=0 to 15 : read(x)
30 doke mem,x : mem=mem+2
40 next lp
50 :
60 setpal start(10) : wait vbl : end
70 :
80 data $000,$111,$222,$333,$444,$555,$666,$777
90 data $000,$111,$222,$333,$444,$555,$666,$777

This routine will copy some values into memory bank 10, and the set the palette from it.

BCLS

Syntax: BCLS ADDR,SCAN
Description: The BCLS command, will erase a set amount of scanlines on any desired bit plane of the screen. The address of the screen is passed in ADDR, and the number of scanlines to erase is passed in the variable ADDR:

10 bcls physic,10

This will clear 10 scanlines on plane 1 of the physical screen. The other planes of the screen can be selected by increasing the screen address by 2.

+0 = Plane 1
+2 = Plane 2
+4 = Plane 3
+6 = Plane 4

MIRROR

Syntax: MIRROR OPT,ADDR,SYPOS,ADDR2,DYPOS,NUM

Description: The mirror command is quite powerful, in that it can mirror parts of the screen, in three ways.. Normal, Halved or Doubled. The mirror option is passed in the OPT variable, and can take the form:

1 = Normal
2 = Half Copy
3 = Double Copy

Variable ADDR is the source address of the image, and SYPOS is the source Y pixel offset. ADDR2 is the destination screen address, and DYPOS is the destination Y pixel offset. NUM is the number of lines to mirror. i.e.

10 mirror 1,physic,0,physic,100,32

This will mirror the STOS key box to the middle of the screen… Try putting it into a loop, and see what happens when you move the mouse over it!

STE Commands

DAC VOLUME

Syntax: DAC VOLUME VOL

Description: This command will set the main volume of the STE sound output to VOL. The input value can take the form 0 – 40 (40 being the loudest).

10 rem ** STE fade out
20 for LP=40 to 0 step -1
30 for LP2=0 to 15 : wait vbl : next LP2
40 dac volume LP : next LP

TREBLE

Syntax: TREBLE TREB

Description: This command sets the amount of treble that is output from the STE sound. TREB can take the form 0 – 12 (0 = -12dB, 6 = 0dB, 12 = +12dB).

BASS

Syntax: BASS BAS
Description: Same as above, but sets the amount of bass instead.

EPLAY

Syntax: EPLAY STRT,LENGTH,SPEED,MODE,PLAYMODE
Description: EPLAY allows hardware sample playing on STE machines, or machines that have the extended sound capability. STRT is the start address of the sample, and LENGTH is the length of the sample. SPEED variable sets the replay speed of the sample, which can be:

0 = 6.258 kHz
1 = 12.517 kHz
2 = 25.033 kHz
3 = 50.066 kHz

The MODE variable sets mono/stereo playback of the sample.. 0 = stereo, 1 = mono. And finally, PLAYMODE can be.. 0 = stop, 1 = play once, 3 = loop forever.

Although the sample replay can be stopped using the EPLAY command, I have also added an ESTOP command, that can also be used to stop the sample.

ESTOP

Syntax: ESTOP

Description: This will stop the hardware sample replay interrupt, and stop ANY sample that is playing under STE hardware.

EPLACE

Syntax: X=EPLACE

Description: This command does the same as the SAM PLACE command that already exists in STOS, however, it returns the address in memory that is currently being played by the STE hardware. It can be used for doing all sorts of nice effects including frequency meters or oscilloscopes:

10 rem ** Oscilloscope routine
20 key off : curs off : hide : mode 1
30 :
40 eplay start(10),102400,1,0,3 : rem ** Play sample, looping
50 :
60 repeat : fastwipe physic
70 for LP=0 to 50 : X=peek(eplace)
80 if X>128 then X=X-255
90 X=X/8 : plot LP,100+X,1 : rem ** Plot sample byte
100 next LP
110 wait vbl : until false

JAR

Syntax: X=JAR
Description: This command will return if a “Cookie Jar” exists on the computer. If a “Cookie Jar” does exist then true (-1) will be return, else 0. This command is to be used in conjunction with the following command:

COOKIE

Syntax: X=COOKIE(STR$)

Description: This command will read the information on the cookie STR$. STR$ must be passed as one of the official Atari Cookies, otherwise no value will be return. A list of all the Cookies and return values follow:

NameValue
_CPUThe number here is the decimal value of the last two digits of the processor present in the machine, indicating which CPU of the 68000 family it is: 00,10,20,30. For instance the value 30 represents 68030 processor
_VDOThe high word of this cookie contains a number from 0 to 2 which indicates what type of video shifter is fitted:

0 = Standard ST video shifter
1 = STE video shifter
2 = TT Graphic chip
_SNDHere it is the bits which tell us about the sound hardware. Bit 0 set indicates the presence of the Yamaha sound chip, bit 1 of the DMA sound chip.
_MCHThis cookie also uses the high word, so that the low word can be used for version changes, and the value describes the overall machine:

0 = Standard ST
1= STE
2 = Mega ST
3 = TT
_SWIThis cookie is used to indicate the positions of the configuration switches on Mega STE’s and TT’s. At present these switches are unused.
_FRBThis longword value will be the address of the FASTRAM buffer, or 0 if no FASTRAM buffers are fitted. This cookie is not found on normal ST & STE machines.
10 if jar then X=cookie("_CPU")
20 print "You have a 680";using "##";x;" Processor"

XPEN

Syntax: X=XPEN
Description: This command returns the x screen position of the STE light pen/gun.

YPEN

Syntax: Y=YPEN
Description: This command returns the y screen position of the STE light pen/gun.

10 repeat
20 if fire then plot xpen,ypen : shoot
30 until false

That’s All Folks!

Yes, that’s all the commands in this great extension.. So go away and have some fun using them.

Bonus!

You may have noticed that there is an extra basic extension on the disk. Well it’s the follow up extension.. GBP II, and this time it’s going to be deadly. The extension is very much like the blitter extension, being in a very early stage. There is no documentation as of yet (it is currently being written). I will however go over a few of the command within this text file so that you can get the feel of the extension and get to use it a little bit.

The extension itself is primarily for use on STE machines, as most of the command are based around the specific hardware. Although some commands will actually work on older ST machines, but it is not really worth running. The commands themselves range from accessing an Atari powerpad/Jaguar controller (both ports!). Centronics/Parallel joystick routines, screen savers, and hardware scrolling.

Credits

Coding and Research : Neil Halliday
Some Commands : Bruno Azzara
Ideas and Design : Neil Halliday, Bruno Azzara & Geoff Harrison
Documentation: Neil Halliday
Demo Programs : Neil Halliday

Other GBP Products

Look out for these other exciting products from GBP, available now from all good public domain libraries.

Slider – a brilliant PD sliding puzzle game with 3 levels.
Rubik’s Magic Strategy – A PD conversion of the brilliant Rubik’s Magic Strategy
Go Moku – A great conversion of the ancient Japanese board game. This program is licenseware, and is only available through Budgie UK.
Playmate II – an interrupt GEM music player that can recognise up to 28 different music formats, and allows you to play music while you work.

Contact Addresses

FOR OBVIOUS REASONS, THIS SECTION HAS NOW BEEN REMOVED FROM THE MANUAL

Neil can also be contacted through the Ad.Lib BBS.. Tel 0191-370-2659. The board is open 24 hours a day, and goes 2400-14400 baud.

Thanks for your interest in this program, and I hope you have lot’s of fun using and exploring the possibilities of STOS basic and GBP.

Neil Halliday and the rest of GBP Software team 1994/1995.
GBP EXTENSION (C)GBP SOFTWARE 1992-1995.

THE END ! ! ! !

S01E02 – Setting Up

For me, making sure our code is structured correctly is an important part of programming. STOS is a very unstructured language; it doesn’t have any kind of function or procedure that we can create to make stand-alone “black box” routines. As a result of this, every variable that is created is global in scope; in other words, if you define a variable, ALL of your program can access that variable.

One of the reasons I created the VS Code STOS extension was to bring at least a little bit of structure to the code structure of STOS. Of course, this is only applicable when viewing the code within the VS Code environment. Once the code is output in the STOS format, it’s pretty ugly, but we really don’t need to worry about that. We can also annotate our code better within VS Code by using the non-transferred comments that only exist in our code editor, and not the final STOS basic file.

So, what we will try to do during this series is create as clean a code set as we can, with lots of comments to explain what is going on for you lovely people that are reading. Hopefully, this will enable you to take the code and do something else with it. I don’t mind what you do with it, but please don’t just distribute it and call it your own. Make sure you create something new and exciting, and all I ask is that you give me a little credit—that’s all.

In this section, we will just do some simple preparations for our main program code. Nothing clever, just the usual housekeeping that we need to do for a STOS program to run how we want.

Creating our Project

This is a simple enough process. Using the command pallet within VS Code, use the function called STOS: The Game Creator: STOS New. This will launch a series of questions about your new STOS project and where to save it on your computer. You can follow detailed instructions on how to do this on the overview page.

Once you’ve got your project created, you will have your main.stos file, which is the main program file that we will be using to write our code.

You will also need to create an area somewhere that either STOS on your ST or your emulator has access to. This is where you will need to copy the project assets and, ultimately, your build.asc file so you can test it out.

Setup Code

When you start a new STOS project in VS Code, you get the initial comments section at the top of the code, just as a reminder that it was me that created the program. So we’re going to remove that and add our first comments as a description of what the project is.

rem *** Manic Miner - STOS Basic Recreation
rem ***
rem *** Written by Neil Halliday / STOS Coders
rem *** June 2023
rem ***
rem *** main.stos : main program file 

So what’s going on there?  Well, the rem command is short for “remark” – in other words, it’s a comment. Having rem on a line of code means that everything after that command is regarded as just a comment and is not any kind of executed code. Even if you had valid code after the rem, it would still be treated as a comment until the next line.

The rem command is a valid STOS command, and is transferred to our final STOS program code, and will be visible within the listing within STOS. We will also be using other types of comments that will not be transferred to STOS, which will be explained later on.

Screen Setup

Now that we have our comments done, we will do some basic setup of the screen. This is to setup how STOS controls the screen and switch off some of the STOS elements that are not required when running a full-screen, low-resolution game.

key off : curs off : click off : flash off : mode 0 : hide on : auto back off : anim off : synchro off
palette $000,$777,$005,$007,$500,$700,$504,$707,$050,$070,$055,$077,$550,$770,$555,$777

Not crazy code, but let’s explain what each of the commands do.

CommandDescription
key offSwitches off the STOS menu system that is shown at the top of the screen. The one that shows you which function keys do what. The window is only really useful when using the editor, and it is always the first to go. You can switch it back on by using the key on command.
curs offIt does exactly what it says on the tin: it switches off the cursor graphic that shows where the text you are going to type appears. Again, this can be turned on using the curs on command.
click offSwitches off that horrible sound that is made when you press a key. I think this sound has to be one of the worst keyboard clicks I’ve ever heard, and I spent many years of programming in STOS with the volume down until I was running stuff. As with all the other commands, click on will switch the keyboard click back on (but why would you!?)
flash offRemoves the standard colour cycling that is performed on the colour of the cursor graphic. Even though we switched the cursor off before, the colour cycle continues, so we need to switch that off to avoid some of our graphics looking like some LSD-driven fantasy dream. You guessed it, flash on switches it back on.
mode 0Sets our screen mode to low resolution. STOS allows us to use all three of the ST screen modes. 0 = low, 1 = medium, and 2 = high resolutions; however, you cannot use mode 2 unless you are plugged into a high resolution monitor, and even then, you don’t need to because modes 0 and 1 don’t work, so it’s automatically in mode 2.
hide onMakes the mouse pointer invisible. Unfortunately, it doesn’t switch off the mouse interrupt, so moving the mouse around still slows down the running program, but at least we can’t see the arrow. There is a command in the Misty extension by Top Notch that switches the mouse interrupt off to stop that from happening. Now, you would think that you would use hide off to get the mouse pointer back? Sorry, here’s a curve ball that Francois threw us! To get the mouse pointer back, you use the show on command. With both of these commands, you can actually remove the on bit, and then it makes a bit more sense: hide and show. Choose which method you prefer, and then stick to it; neither is right nor wrong.
anim offThis makes sure that any STOS sprite animations are switched off. We won’t be using these as they are quite slow; instead, we will be doing something a little bit more interesting to make things run more quickly.
synchro offSwitches off the STOS sprite synchronisation interrupt. Anything we can switch off STOS-wise is always a good thing!

Our second line of code sets the colour palette of the screen. It is simply a list of colours in colour number order, starting with colour 0 (the background and border colour). You don’t have to specify all the colours when you call the command, but you always have to specify them in the correct order. Note: The standard palette command in STOS is not compatible with the STE, so your number ranges here have to conform to the standard ST 512 colour palette numbers ($000 to $777).

Loading Our Assets

What are assets? They are the things that we use within our program, such as sprites and sound effects. They are loaded into STOS memory banks for use during the program’s execution. Lots of programs perform data loads at different points in the game to conserve memory, but as our program is going to be super small anyway, we will just load them upfront. We’re targeting a 512kb ST for this program, so if we start to run out of memory, we can refactor this bit, but we should be fine.

To cater for any refactoring that may be required, we’ll put our loading of assets code in a separate place. We already know that we don’t have procedures within STOS, so we will do the next best thing. We will use a VS Code label and create a subroutine. Now we can call it whenever we want, from wherever we want.

It’s not a perfect way of doing this, but it’s the best we have, and it works well for my needs.

@LoadAssets
erase 1 : load "MANIC.MBK",1
return
CommandDescription
@LoadAssetsThe identifier that tells the VS Code transpiler that this is a label. As a result, during the conversion to STOS code, the line is converted to a rem line, and a record is made of the line number where it sits. A second conversion pass then happens and replaces anywhere that references the label with the appropriate line number.
erase 1If we already have something in STOS memory bank 1, it will be deleted and space cleared for our new data.
load “MANIC.MBK”,1Loads the MBK (memory bank) file into STOS bank 1. The MANIC.MBK is our STOS Sprite bank, which always resides in bank 1 of STOS.
returnReturns to the point where the call to @LoadAssets was made.

We now need to make a call to our @LoadAssets subroutine. This is done using the STOS gosub command, and we can make a call to that at the beginning of our program. So, we add the line to the top of our program after the call to the palette command.

gosub @LoadAssets

And that’s it! We’re done for now. We have our screen setup, and we have our assets loaded. If you want, you can transpile the program, load it into STOS and run it. You won’t get much out of it other than a blank screen and an “OK” at the bottom, but at least you know it’s working.

The Game Loop

Probably the single most important element of any game is it’s gameplay loop. This is the section of code that is continually executed during game play and allows us to control, well, everything. If we want to create a game that is as flexible as possible and easy to understand what is going on, I highly recommend making your game loop as simple as possible. I approach this by writing specific sections of code that perform specific game logic tasks. With a language like STOS, this can become quite messy, but using the VS Code development environment really helps us here. So, let’s create our game loop.

repeat
    gosub @WaitVBL
until false

Pretty simple right? Well, yeah it will be at this point in time, but as you can see, I’m making a call to the @WaitVBL subroutine. By using this technique, we can make our game loop very simple and add new subroutines and call them from our game loop without making things ugly in terms of code. So, what’s going on in our game loop?

CommandDescription
repeatThe repeat command forms part of a repeat/until loop, which can be simply explained as follows… “repeat this next section of code until a certain condition is met.”
gosub @WaitVBLThe gosub command tells STOS to “goto” a “subroutine” – in this case, the @WaitVBL subroutine. Normally in STOS basic, the @WaitVBL section would be a line number; but because we are working in VS Code, the transplier will automatically convert @WaitVBL in to the relevant line number for us. This makes our code much simpler to read.

Note: All subroutines must terminate with a return command so that STOS can go back to where it called it from.
until falseThis is an interesting one. Here we are specifying the condition for the loop to stop. Every condition within the basic language equates to either true or false. This could be something like (1 = 1), which equates to true; (1 = 0) would always equate to false. Because we are just using false, there is no condition; it’s just always false. Therefore, our loop will just go on forever. In STOS true is represented by the number -1, and false represented by the number 0.

We will amend this statement later in our development cycle because we will need to take into account a game-over scenario.

Now we need to put the code in place for our @WaitVBL subroutine.

@WaitVBL
screen swap : doke $ff8240,$222 : wait vbl : doke $ff8240,$000
return
CommandDescription
screen swapSwaps the addresses of the physical and logical screens. This is also called “double buffering“. The idea behind this is that you display all your graphics off screen, and when the time is right, you swap the screen to show what has just been displayed. Then, while that display is showing, you draw your new updates on the hidden screen, and again swap it to show your new display when you are done. This prevents your objects from being displayed as you are drawing them and prevents flicker on the screen. Good eh?
doke $ff8240,$222Eh? Er? What? Just what is going on here?

Well, it’s quite simple to explain. Memory address $ff8240 is where the ST reads the value that represents what colour 0 is set to (the border, or background). By doking a value (doke is when we want to store a 16-bit value, also known as a word, into a memory address) we are setting the border to $222, which is a dark grey colour. You’ll see why we do this in a second.
wait vblThis command forces STOS to wait for the next vertical blank (VBL). In other words, it stops the execution of any other code until the vertical blank returns to the top of the screen. The use of the wait vbl command is what ensures we can create a consistent speed of game play and reduce flicker.
doke $ff8240,$000As with the other doke command, we are directly setting the border colour, but this time we are setting it back to black.

So why do we change the colour of the border before and after the wait vbl command?  Well, this is a clever little trick that let’s us see how quickly our game is running. The border will be black before all our routines are called, and it will then turn grey for the amount of time that it takes for the vertical blank to return to the top of the screen. The larger the section of black, the more CPU time is being used to perform our code.

The Atari ST in low and medium resolutions run at either 50hz or 60hz depending on where you are located in the world. Therefore, the VBL returns to the top of the screen either 50 or 60 times per second. In other words, the screen redraws at 50 or 60 frames per second. Most games on the ST run at half this speed, and it’s quite rare to actually find a game that runs at full frame rate on a standard ST. Our aim is to make sure everything runs in a single VBL; however, this is particularly hard to achieve when writing programs in STOS. But let’s see what we can do. If your area flashes a lot, it’s likely you are over a single VBL, whereas if the grey colour is solid – good job, you’re running at 50/60 frames per second!

That’s All For Today

That’s all that we are going to cover in this section. Join us in episode three, where we will look at getting Miner Willy to walk and jump.

S01E01 – What is Manic Miner?

So, what is Manic Miner?  Perhaps you have been living under a rock, or perhaps you are just not old enough to remember the game that changed how we view platform games. For those who don’t know what Manic Miner is, let me explain…

Manic Miner is a platform based game written for the ZX Spectrum by a [then] 16 year-old Matthew Smith and published by Bug-Byte in 1983. It was republished later in the same year by Software Projects, a company set up by Matthew, Alan Maton, and Colin Roach. Although Matthew worked as a freelance developer for Bug-Byte, an oversight in his contract enabled him to take Manic Miner with him when he left – ooops! There is a fantastic documentary on Matthew Smith produced by Kim Justice on YouTube. I highly recommend you watch it.

Inspired by Miner 2049er which was published for the Atari 8-bit family in 1982, Manic Miner has been called one of the most influential platform games of all time. It has been ported to many home computers, consoles, and mobile devices.

To get a more full flavour of Manic Miner, check out the full walkthrough video on YouTube publish by RZX Archive. You might want to turn the volume down after a few minutes to save your ears from bleeding! That said, Manic Miner was the first ZX Spectrum game to feature in-game music combined with sound effects while you played!

Game Play

You control Miner Willy as he attempts to escape from 20 different caverns by collecting various flashing objects to activate the exit portal. Once the portal is activated, you must jump into it to progress to the next cavern. All this must be done before your oxygen supply runs out and while avoiding various enemies and deadly obstacles that wander around the caverns along predefined paths. Once you have successfully traversed all 20 caverns, the game starts again from the beginning. Scoring is based on the objects that you collect, and bonus points are awarded for the amount of oxygen you have when you reach the portal. An extra life is awarded for each 10,000 points scored.

Deconstructing the Game

As with any game you want to create, you need a plan. In our instance, this process is a little easier because we already have a finished game that we can reference. But we do need to break the game down into it’s component parts so we understand what we need to do and can identify any challenges that we may experience during the programming. From this, we can plan our STOS activities and hopefully cater for any hurdles before they become problematic – there’s nothing worse than getting part way through something and realising “Oh crap! How can we deal with that?” and having to refactor a load of stuff.

Gameplay Graphics

Let’s have a look at the Manic Miner graphics and how we are going to use them.

Miner Willy

Our hero is a fairly simple single colour 10×16 sprite with a total of 4 animation frames to cover walking and jumping in one direction. What is going to be really interesting about the Miner Willy sprite is the hot spot positioning – Manic Miner has some pretty serious pixel-perfect jumps that need to be made. I hope we can replicate them appropriately.

STOS has a minimum sprite width of 16 pixels, so we will need to define Miner Willy as a 16×16 sprite, and it will take the first 8 sprites in our STOS sprite bank. Four sprites for walking to the right, and four sprites for walking to the left. There are no individual jumping sprites, as they are just the same walking sprites with a vertical movement.

His starting point in the game is usually the same, regardless of the level, which is 16,112. There are a couple of levels where this changes, but it’s not very often.

Walls & Floors

There are various wall & floor graphics throughout the game that stop Miner Willy from walking to certain places within the level; however, the common factor is that they are all 8×8 pixels and follow repeatable patterns. This makes it nice and easy for us to replicate these elements. Something that we need to note is that some floors dissolve when Miner Willy stands on them for too long.

We will need to compensate for the fact that STOS can only handle a minimum of 16 pixels wide for a sprite. Not a problem, because although we are going to store these images in a sprite bank, we are going to do something whacky with them later on!

Baddies

There are various different types of baddies, and they are all of varying size too. The one common factor of them is they all have 8 frames of animation (4 left & 4 right) and either move up/down or left/right. They move on a set pattern, but are not necessarily linked to the floors they are placed on. Each screen has a varying number of baddies. If Miner Willy hits a baddie, a life is lost and the level resets back to the beginning.

Obstacles

As with the walls, all obstacles are 8×8 pixels and come in the form of stalactites, stalagmites, cactus plants, and other varieties of nastiness to jump over. One touch of these obstacles, and you lose a life, and the level resets back to the beginning.

Collectables

For Miner Willy to progress to the next cavern, all the flashing collectable objects need to be picked up. All collectables are 8×8 pixels, and within the game the foreground colour attribute is rapidly switched to make them flash. We will draw these elements using colour 1 as we can apply a STOS colour cycle without affecting any of the other colours on the screen – bonus!

Exit Portals

Exit portals come in various different styles, and are 16×16 in size. They have a simple inverse flash animation that made use of the ZX Spectrum foreground and background flash function – So we need to make sure we get the timing of this function correct. As we don’t have a flash function in STOS, we will either need to use a colour cycle, or a simple inverse sprite. I’ve opted for a second sprite as this means that we don’t have to worry about palettes or anything like that for a colour cycle.

Level Breakdown

Let’s break down the different levels that Manic Miner has. This way, we know what we have to achieve. We’ll start with “Central Cavern” (level 1) first of all, and we’ll add to this list as we progress with the episodes.

Level 1 – Central Cavern
ElementDescription
Solid FloorBright Red
Dissolving FloorDark Red
Number of Baddies1
Baddy Types1 x Yellow Trumpet Robot – L/R
Number of Collectables5 x Keys
ObstaclesBlue Stalactite
Green Cactus
Exit PortalBlue / Yellow in bottom right

Ok, I think we are about ready to start putting some code together. So check out Episode 2 to take a look at how we are going to get things setup.

Games Coding Competition 2020

Games Coding Competition 2020

Welcome to the STOS Coders Games Coding Competition 2020!  This year we have decided to challenge all you clever people in three different categories of games. Enter as many games as you like, in as many different categories as you like. Just remember, each entry can only qualify for one of the categories. Make sure you are familiar with the categories and the rules which are listed below. So, choose your category, and get coding!

One Liner

Can you write a game in just one line of code? Yes, you heard it, one line of code!  It’s been done before with some great results – let’s see if it can be done again.

Ten Liner

Ten times the lines, ten times the gaming fun? Let’s find out if it means just that. Write a complete game in ten lines of code.

One of Everything

The one of everything category means just that! You can have an unlimited amount of code, BUT your .BAS file is limited to one of each resource at program load. So you could for example have:

  • 1 sprite
  • 1 bob
  • 1 joey
  • 1 PI1/PC1 file
  • 1 NEO file
  • 1 Tile
  • 1 Map file
  • 1 Tracker file
  • 1 Chip Music file
  • 1 Sample sound
  • 1 Sound effect

But that is it!  It doesn’t matter how many memory banks you use – one massive one or lots of little ones – you can only have one of each type of asset in your game, not matter where it is stored! If you want to generate new resources whilst your game is starting up or during gameplay, no problem, go ahead, but your game must be up & running and ready to play within 30 seconds (when compiled) of running the file. The loading of asset files does not count towards your 30 seconds.

Guest Judge

This year we are pleased to welcome Francois Lionet as our very special guest judge. As many of you know, Francois is the creator of STOS and therefore is perfectly placed to select his winner from all the entries. He will be judging them on sound, graphics, originality and most importantly ingenuity and playability.

Prizes

There are some cool prizes up for grabs for the winners of each category. We also will be creating a compilation disk of all the entries once the winners have been chosen. Keep watching the website and Facebook as we reveal more details in the weeks ahead.

Submission

You must submit your entry to STOS Coders by 31st October 2020. E-mail your submissions to games@stoscoders.com including the following information:

  • Your name
  • The category which you want to enter
  • Your game attached as an uncompiled .BAS file
  • List of extensions used, along with their file extensions (if you have changed them) – even better if you can include a zipped copy of your STOS folder!
  • Anything else you think is relevant to your submission

All entries must be received before 23:59 (UK time) on 31st October 2020 to qualify.

The Rules

  1. You may enter as many different games as you like.
  2. You must tell us which category you are entering your game in. All games outside of the stated parameters for your chosen category will be disqualified. The adjudicators decision is final.
  3. Each game can only qualify for 1 category.
  4. All games must be “compilable”.
  5. Use as many data banks as you like, there are no restrictions, so long as the memory usage of the game sticks to the rules.
  6. No databanks can existing in your source code, you must load them from disk at runtime.
  7. Use whatever extensions you like, heck if you need to write a new extension for you game, even better! Tell us which extensions you are using though, and what file extension you are using so that we can create the correct environment to run them in.
  8. You can’t have an extension that just does everything. For example “10 runmygame” is not allowed.
  9. Everything has to be written using 100% STOS and extension commands.
  10. No assembler helper routines can be used.
  11. Your game must run on a 1Mb (or below) Atari ST or STE machine running at the standard 8Mhz.
  12. You give permission for STOS Coders to publish videos of your game on social media platforms (such as the STOS Coders Facebook Page), STOS Coders Websites and STOS Coders YouTube channels.
  13. You give permission for STOS Coders to distribute the source code of your game and any extension via social media platforms (such as the STOS Coders Facebook Page), STOS Coders Websites and STOS Coders YouTube channels as a standalone application, or part of a compilation.
  14. All entries must be received by October 31st 2020.
  15. The judges decision is final.
  16. Winners will be announced during November 2020.

Happy STOSing!