Personal Computer News
8th September 1984
Author: Clare Gurton
Published in Personal Computer News #077
Scroll the Amstrad screen display with Clare Gurton's simple machine code routines.
Amstrad Scrollwork
Scroll the Amstrad screen display with Clare Gurton's simple machine code routines
It's often useful to scroll a computer's screen display up or down, particularly in games. On most machines you can scroll the screen up a line by printing in the last screen cell, but there's no way you can reverse the process to scroll the screen downwards.
Of course, it's possible to write machine code routines to shift the screen display about according to your needs, but this means getting to grips with assembly language programming, and most of us would rather steer clear of that.
Fortunately, the Amstrad ROM is so well-documented that it's possible to write very simple machine code routines which call up ROM routines to do such complex tasks.
For example, there's a ROM routine called SCR HW ROLL at &BD4C (50395 decimal) which uses the Amstrad's hardware to scroll the screen up or down, depending on the contents of the B register. The ROM routine moves the screen by eight pixel lines (one character row) at a time, and fills the new top or bottom row with the colour given by the ink code of the contents of the A register. If the contents of the B register is zero, the screen scrolls downwards, and any non-zero value forces an upwards scroll.
There are two simple ways of using the ROM routine. Either you can set up a single routine which you have to change by one byte (via a POKE) before calling it, or you can set up two routines, one for up, the other for down, and that's the first method shown here.
Each routine will first load the B register with the relevant value - 255 for up, 0 for down - then call the ROM routine and finally return to Basic. Loading the B register uses the mnemonic LD B,n (where n is a number between 0 and 255). The opcode for LD B is 06, so this will be the first byte of each of the assembled routines. For the down-scroll routine the next number will be zero, while the up-scroll routine will need any non-zero value - FF (255 decimal) will do.
Calling the ROM routine requries the mnemonic CALL nn, op-code C9, 205 decimal. The ROM routine begins at &BC4D (50395 decimal) and, because the Z80 works on the low byte, high byte principle for referring to 16-bit addresses, we'll need to follow the CALL with 4D (77 decimal) then BC (188 decimal). Finally, the routines must return to Basic via the mnemonic RET, op-code CD, 201 decimal.
The Basic loader for the two routines is given in Figure 1 below.
You can RUN this, then delete it before entering a program which uses scrolling. You'll have to set memory to 43879 to preserve the routines from corruption by Basic programs and their data.
Address | Mnemonic | Opcode/Data | Decimal |
43880 | LD B,FF | 06 | 6 |
43881 | FF | 255 | |
43882 | CALL BC4D | C9 | 205 |
43883 | 4D | 77 | |
43884 | BC | 188 | |
43885 | RET | CD | 201 |
43886 | LD B,00 | 06 | 6 |
43887 | 00 | 0 | |
43888 | CALL &BC4D | C9 | 205 |
43889 | 4D | 77 | |
43990 | BC | 188 | |
43991 | RET | CD | 201 |
Figure 2. Addresses, mnemonics, opcodes/data and decimal values |
10 MEMORY 43879
20 up.scroll=43880:down.scroll=43886
30 WHILE A$ = ""
40 FOR up = 1 TO 10
50 CALL up.scroll
60 NEXT
70 FOR down = 1 TO 10
80 CALL down.scroll
90 NEXT
100 A$ = INKEY$
110 WEND
Figure 3. Demonstration of routines
The routines are assembled from 43880CALL 43880 will scroll the screen up, while CALL 43886 will perform a downwards scroll. You may find it easier to set up two variables such as: down.scroll = 43880; then use CALL up.scroll and CALL down.scroll. Figure 3 demonstrates how to use the routines like this.
Address | Mnemonic | Opcode/Data | Decimal |
43870 | LD B,A | 06 | 6 |
43871 | A | 10 | |
43872 | LD A,0 | 3E | 62 |
43873 | 0 | 0 | |
43874 | PUSH BC | C5 | 197 |
43875 | PUSH AF | F5 | 245 |
43876 | LD B,FF | 06 | 6 |
43877 | FF | 255 | |
43878 | CALL &BC4D | CD | 205 |
43879 | 4D | 77 | |
43880 | BC | 188 | |
43881 | POP AF | F1 | 241 |
43882 | POP BC | C1 | 193 |
43883 | DJNZ | 10 | 16 |
43884 | F5 | 245 | |
43885 | RET | C9 | 201 |
Figure 4. Multiple Scrolls |
Figure 4 gives the details of a rather more complex, single machine code routine which will repeatedly call SCR HW ROLL according to the contents of address 43871. As given, the routine uses an ink value of zero and scrolls the screen up by ten lines.
You can alter any of these by POKEs to the relevant addresses. You can POKE 43877 with 0 for a down scroll, 255 for up, while the ink colour for the newly created top or bottom line is held in 43873. So, if you wanted the screen to scroll up ten times you'd POKE 43877,255:POKE 43871,10:CALL 43870. The last listing (Figure 5) is the Basic loader for the routine.
This routine uses the B register as a sort of loop counter. The routine begins by loading the B register with the contents of address 43871, which is why you have to poke this address with an eight bit number (<=255) for the number of lines to scroll.
Next, the colour for the blank line which will be created by a scroll in either direction is loaded into the A register. Both A and B have to be PUSHed onto the stack before calling the ROM routine because it corrupts all the register pairs. The last action before calling SCR HW ROLL is to load the B register with the direction in which to scroll the screen.
When the ROM routine returns, the AF and BC register pairs have to be restored (POPed) for the next operation. This is the Z80 instruction DJNZ - decrement and jump on non-zero. This subtracts one from the B register and jumps according to the displacement (two's complement) in the next byte if the new content of the B register is not zero. When the DJNZ operation results in zero, the zero flag is set, the test fails and the routine ends in RET, passing control back to Basic.
Ideally, you'd use an assembler to work out the displacement for you, but until one is available for the Amstrad you'll just have to hand-assemble awkward details like this.
In this example the program flow needs to be directed back to address 43874, which is nine bytes back from the address of the DJNZ instruction. However, you have to add 2 to this value, because the program counter (PC) will be pointing to two bytes on from the DJNZ instruction by the time the decrement etc has been done. This means you want a negative jump of 11 bytes.
To translate a negative decimal number like -11 into its two's complement, write down the binary pattern (00001011), rewrite it with all noughts replaced with ones and vice versa (11110100). Then add one to the binary pattern (11110101) and finally convert to decimal (245) which is the value you put at address 43884 as the negative displacement for the DJNZ instruction.
10 MEMORY 43869
20 address = 43869
30 scroll = 43870
40 DATA 6,10,62,0,197,245,6,255,205,77
50 DATA 188,241,193,16,245,201
60 FOR count = 1 TO 16
70 READ value
80 POKE address + count,value
90 NEXT
100 '
110 ' 43873 is colour of new line
120 ' 43871 is number of scroll lines
130 ' 43877 is up/down
140 '
150 REM Demonstrations
160 GOSUB 210
170 POKE 43871,10
180 GOSUB 300
190 END
200 ' Demo One
210 FOR no.lines = 1 TO 20
220 POKE 43871,no.lines
230 POKE 43877,0
240 CALL scroll
250 POKE 43877,255
260 CALL scroll
270 NEXT
280 RETURN
290 ' Demo Two
300 FOR colour = 1 TO 255
310 POKE 43877,0
320 CALL scroll
330 POKE 43877,255
340 CALL scroll
350 POKE 43873,colour
360 NEXT
370 RETURN
Figure 5 also gives demonstrations of the routines. You'll find that altering the value contained in the A register results in some interesting coloured textures.
This article was converted to a web page from the following pages of Personal Computer News #077.