In the previous article, we created a tile map and did a simple scrolling screen using Screen Copy.
While it worked, and demonstrated some cool aspects of AMOS, it wouldn’t really be efficient for use in an action/arcade game, is there a better option? Also, can we load the map from disk?
There is also another option for scrolling the screen we haven’t investigated, let’s look at that first.
Def Scroll
AMOS has a neat feature where you can define scrollable areas, and then specify which area you wish to scroll.
When I think of parts of the screen scrolling, my mind immediately goes to the kind of parallax scrolling where the strip of background closest to you scrolls faster than the most distant.
Here is a simple example:

Here is the code to implement this effect:
' Set up the screen
Screen Open 1,320,200,32,Lowres
Screen Open 0,320,200,32,Lowres
Flash Off : Hide : Paper 0 : Pen 4 : Cls
' Define the scrolling
Def Scroll 1,0,0 To 320,8,-1,0
Def Scroll 2,0,8 To 320,16,-2,0
Def Scroll 3,0,16 To 320,24,-4,0
Def Scroll 4,0,24 To 320,32,-8,0
Def Scroll 5,0,32 To 320,56,-16,0
' Put something on screen
For LINE=1 To 15
Print "Hello World! ";
Next LINE
' Scroll Loop
While Inkey$=""
Screen Copy 0,0,0,16,60 To 1,0,0
Wait Vbl
Scroll 1
Scroll 2
Scroll 3
Scroll 4
Scroll 5
Screen Copy 1,0,0,16,60 To 0,304,0
Wend
The main part to take note of is where we “define the scrolling”:
Def Scroll 1,0,0 To 320,8,-1,0
This tells AMOS that we want to set up scrolling area 1, that it starts in the top, left, goes as far as the right of our screen, but only 8 lines down. The scroll direction is -1, so it will scroll left one pixel. On the vertical it doesn’t scroll at all.
We also set up another four zones, getting incrementally faster.
To actually activate the scroll we have to enter
Scroll 1
Where 1 is the number of the zone to scroll. Each time this is called, it will scroll that area of screen by the amount we set up in the definition.
You might also notice that to make it an infinite scroll, we copy the area of screen we are about to scroll off screen to a hidden screen and back again. Otherwise as the screen scrolls we would have a gap where the text used to be.
Under the hood, the Def Scroll approach is simply using the blitter to screen copy in a similar way to what we implemented in the previous tutorial.
Is there a better way?
I am no expert, I only got an Amiga for the first time in 2020, so I am still learning. That said, I have to believe copying large amounts of data on each frame is not going to be the best route.
So far my best results have come from mixing the first example we looked at, using screen offset, and the tile-based approach.
Scrolling Tiles with Offset
Before we get into the code, I need to drop some theory.

If you recall, setting the screen offset moves our viewport, rather than the screen data. It’s like moving your phone camera and seeing the updated view on your phone display. The scene didn’t move, your view of it did.
So we set up a screen much larger than the viewable screen on your monitor, then to get the scroll effect we move the viewport up this virtual screen. When we get to the top we start over at the bottom.
Obviously, like in the example at the top of the page, we need to take care of the gaps left behind, and make it so there doesn’t seem to be a jolting jump.
For now I take a full copy of the screen and paste it down at the bottom before starting the scroll area. Not the best but it’s progress!
I am not entirely sure if the screen copy approach I use in my example code below is better or if I should have tried pasting the tiles twice instead. Let me know if you have a better approach!
AMOS and Loading the Map Data File from Amiga Disk Drive
Before we get into the code, we should talk about loading the map and how AMOS handles text file access.
We set up a simple ASCII text file with our map data, that way for now we can use a text editor to edit our map.
MAPDATA$="vertical_map_2.txt"
Then later we open the file and start reading it, line by line
' Load the map file from disk
Open In 1,MAPDATA$
' Read the lines and fill the map array
MAP_TILE_ROW=0
Print "Loading ..."
While Not Eof(1)
' Read a line at a time
Print MAP_TILE_ROW," ";
Line Input #1,THIS_LINE$
As each line is read it, we interpret the string by grabbing characters using Mid$ (each tile number is 3 digits, eg. 004, followed by a space).
We increment the row counter and loop, until the end of the file in which case we close the file and continue.
MAP(9,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,37,4))
Inc MAP_TILE_ROW
Wend
' Close the file
Close 1
It’s not super fast, but it is convenient and human-readable. There are other options available to us, but it works!
Full Code
The code is still quite raw, proof of concept rather than a finished piece, but hopefully it gives you an idea as a starting point for how a vertically scrolling shooter can have backdrop implemented.
Rem **************************************
Rem
Rem Vertically scrolling shooter demo
Rem
Rem by Maker Hacks
Rem
Rem 2020
Rem
Rem ***************************************
' Need a bigger buffer for a large map
Set Buffer 150
' Make a double-height screen + 32px
Screen Open 0,320,512+32,16,Lowres
Screen Hide : Curs Off : Flash Off : Cls 0
Colour Back $0 : Hide : Screen Show
Screen Display 0,135,60,320,200
' Debug/Score info area
Screen Open 1,320,20,16,Lowres
Screen Display 1,135,40,320,20
Hide : Curs Off : Flash Off : Cls 13
Screen 0
' Load map tiles
Load "tile.abk"
' Load sprites
Load "invader.abk"
Get Sprite Palette
' Map file (ascii for now)
MAPDATA$="vertical_map_2.txt"
' Set up a list of colours
Palette ,,,,,,,,,,,,,,,,Colour(0),Colour(1),Colour(2),Colour(3),Colour(4),Colour(5),Colour(6),Colour(7),Colour(8),Colour(9),Colour(10),Colour(11),Colour(12),Colour(13),Colour(14),Colour(15)
' DEBUG the palette
X=0
For I=0 To 15
Ink I,I,I
X=X+8
Bar X,40 To X+8,48
Next
' Background to black
Paper 0
' Print colours and numbers
For I=0 To 15
Pen I
Print I;
Next
' See the bob on screen
Paste Bob 170,100,2
'----------------------
' Title screen pause
Locate 15,12
Print "Press a key"
Wait Key
Cls
'----------------------
' Set up map in memory
' Fixed map length for now
MAP_HEIGHT=100
Dim MAP(9,MAP_HEIGHT)
' Globals - not elegant but works
Global MAP_HEIGHT,PLYER_X,PLYER_Y,ENEMY_X,ENEMY_Y,SCENERY_X,SCENERY_Y
Global _SCROLL_OFFSET,MAP(),MAP_TILE_ROW,MAPDATA$,THIS_PIXEL_ROW,ROW_COUNTER
' Initialise environment
INIT
' Where the magic happens
MAIN
Rem Initial setup
Procedure INIT
' Load and Draw the initial map
MAP_INIT
' Initial coords for the player sprite
PLYER_X=150 : PLYER_Y=200
' Initial coords for enemy sprite
ENEMY_X=100 : ENEMY_Y=10
' Start line to draw at
THIS_PIXEL_ROW=288
' Set the screen to start conditions
RESTART_SCROLL
' Uncomment to turn off automatic Bob updates for speed
'Bob Update Off
End Proc
Rem Tile painting routine
Procedure _PAINT_ROW
' Paint a full row of tiles
Paste Icon 0,THIS_PIXEL_ROW,MAP(0,MAP_TILE_ROW)
Paste Icon 32,THIS_PIXEL_ROW,MAP(1,MAP_TILE_ROW)
Paste Icon 64,THIS_PIXEL_ROW,MAP(2,MAP_TILE_ROW)
Paste Icon 96,THIS_PIXEL_ROW,MAP(3,MAP_TILE_ROW)
Paste Icon 128,THIS_PIXEL_ROW,MAP(4,MAP_TILE_ROW)
Paste Icon 160,THIS_PIXEL_ROW,MAP(5,MAP_TILE_ROW)
Paste Icon 192,THIS_PIXEL_ROW,MAP(6,MAP_TILE_ROW)
Paste Icon 224,THIS_PIXEL_ROW,MAP(7,MAP_TILE_ROW)
Paste Icon 256,THIS_PIXEL_ROW,MAP(8,MAP_TILE_ROW)
Paste Icon 288,THIS_PIXEL_ROW,MAP(9,MAP_TILE_ROW)
End Proc
Rem Map loading and drawing routine
Procedure MAP_INIT
' Load the map file from disk
Open In 1,MAPDATA$
' Read the lines and fill the map array
MAP_TILE_ROW=0
Print "Loading ..."
While Not Eof(1)
' Read a line at a time
Print MAP_TILE_ROW," ";
Line Input #1,THIS_LINE$
' Put the file data into the array
MAP(0,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,1,4))
MAP(1,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,5,4))
MAP(2,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,9,4))
MAP(3,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,13,4))
MAP(4,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,17,4))
MAP(5,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,21,4))
MAP(6,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,25,4))
MAP(7,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,29,4))
MAP(8,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,33,4))
MAP(9,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,37,4))
Inc MAP_TILE_ROW
Wend
' Close the file
Close 1
' LOADED LINES = NEW MAP HEIGHT
MAP_HEIGHT=MAP_TILE_ROW
' Select the start tile row to put on screen
MAP_TILE_ROW=MAP_TILE_ROW-16
' Screen full of tiles
For THIS_PIXEL_ROW=1 To 512 Step 32
' Paint the row of tiles
'_PAINT_ROW
' Increment the tile row
Inc MAP_TILE_ROW
Next THIS_PIXEL_ROW
End Proc
Rem Main loop
Procedure MAIN
' Loop forever unless told otherwise ...
Repeat
' Debug info
Screen 1 : Pen 2 : Paper 0
Locate 0,0 : Print " "
Locate 0,0 : Print "OFFSET: ",_SCROLL_OFFSET," ROW: ",MAP_TILE_ROW
Screen 0
' Get player input
_PLAYER_INPUT
' Decrement the scroll offset
Add _SCROLL_OFFSET,-2
' If we get to zero then reset the scroll, otherwise
' scroll by setting the screen offset
If _SCROLL_OFFSET>0
' Paint row of tiles above viewable area
Add ROW_COUNTER,2
If ROW_COUNTER>=32
ROW_COUNTER=0
Dec MAP_TILE_ROW
THIS_PIXEL_ROW=_SCROLL_OFFSET-32
_PAINT_ROW
End If
' The offset is how we get the scrolling
Screen Offset 0,0,_SCROLL_OFFSET
Else
' We got to the top of the virtual screen
' So we need to start over at the bottom
Bob Off
Screen Copy 0,0,-32,320,200 To 0,0,256
RESTART_SCROLL
Dec MAP_TILE_ROW
THIS_PIXEL_ROW=_SCROLL_OFFSET-32
_PAINT_ROW
Screen Offset 0,0,_SCROLL_OFFSET
End If
' Draw player and enemy sprites
SPR_DRAW
' Wait for vertical blank
Wait Vbl
' If mouse pressed then quit, otherwise back to the loop!
Until Mouse Key
End Proc
' Reset the screen offset
Procedure RESTART_SCROLL
' Screen offset counter
_SCROLL_OFFSET=288
' Reset screen offset to screen bottom
Screen Offset 0,0,_SCROLL_OFFSET
' Set the row in MAP array to count from
If MAP_TILE_ROW=<10 Then MAP_TILE_ROW=MAP_HEIGHT-1
' Reset the pixel row counter
ROW_COUNTER=0
End Proc
Rem Get player input
Procedure _PLAYER_INPUT
' If cursor left or right then move player
If Key State(79)=True and PLYER_X>112 Then PLYER_X=PLYER_X-4
If Key State(78)=True and PLYER_X<418 Then PLYER_X=PLYER_X+4
' Fire button
If Key State(64)=True Then Shoot
End Proc
' Draw player and enemy sprites
Procedure SPR_DRAW
' Set the obstacle x, y
SCENERY_Y=512-_SCROLL_OFFSET+4
If SCENERY_Y>512 Then SCENERY_X=Rnd(288)
' Bobs are drawn onto the screen memory
Bob 10,SCENERY_X,SCENERY_Y,4
Bob 20,SCENERY_X,SCENERY_Y+32,5
Bob 30,SCENERY_X+32,SCENERY_Y,6
Bob 40,SCENERY_X+32,SCENERY_Y+32,7
' Sprites are on a layer above the regular
' viewable screen so are much easier
Sprite 0,PLYER_X,PLYER_Y,2
' Sprite 8 upwards is a special "computed" sprite
Sprite 8,ENEMY_X+100,ENEMY_Y,1
' Move enemy down the screen
Add ENEMY_Y,6
' If the enemy drops off bottom
' start at a new random X coord
If ENEMY_Y>300
ENEMY_X=Rnd(300)+30
ENEMY_Y=30
End If
End Proc