{"id":4585,"date":"2023-02-25T15:56:12","date_gmt":"2023-02-25T15:56:12","guid":{"rendered":"https:\/\/www.petecodes.co.uk\/?p=4585"},"modified":"2023-03-13T12:52:54","modified_gmt":"2023-03-13T12:52:54","slug":"graphics-in-assembler-for-the-sam-coupe","status":"publish","type":"post","link":"https:\/\/www.petecodes.co.uk\/graphics-in-assembler-for-the-sam-coupe\/","title":{"rendered":"Graphics in Assembler for the SAM Coup\u00e8"},"content":{"rendered":"\n

Introduction<\/h1>\n\n\n\n

The Next Step<\/h2>\n\n\n\n

Continuing my series on Assembler for the SAM Coup\u00e9, where in the first post<\/a> we introduced the various principles required to get started with Assembly Language Programming. We\u2019ll now be looking at Graphics and Sprites and how to start showing Graphics on the screen.<\/p>\n\n\n\n

If you haven\u2019t caught up on the first post in this series, you can find the first post in the series here.<\/a><\/p>\n\n\n\n

A New Sprite Editor<\/h2>\n\n\n\n

Since creating this post, I’ve created a really basic Sprite Editor that produces the required Sprite Data for this tutorial, so you can go on and show some far prettier sprites.<\/p>\n\n\n\n

\"\"<\/a>
SAM Coupe Sprite Editor<\/figcaption><\/figure>\n\n\n\n

You can find the SAM Coupe Sprite Editor here;<\/p>\n\n\n\n

https:\/\/www.petecodes.co.uk\/sam-coupe-sprite-editor<\/a><\/p>\n\n\n\n

It should be easy enough to get your head around… Just pick a colour, start clicking on pixels to create your Sprite. Then hit the Generate Sprite Data button.<\/p>\n\n\n\n

At teh bottom of the page will be your Sprite Data.<\/p>\n\n\n\n

Select all the text and copy that into your asm file (or over the top of the existing Sprite Data) and you’re good to go!<\/p>\n\n\n\n

A Quick Word of Thanks<\/h1>\n\n\n\n

This has been quite a complicated blog post to write and get right, some of the topics needed a bit of research. It certainly wouldn\u2019t of been possible without the help of the people in the Facebook SAM Coupe Users Group<\/a>. Special mentions (in no particular order) go out to; Andrew Collier, Frode Tenneb\u00f8, Graham Clemo, Stefan Drissen, Anton Javor\u010dek, Simon Owen, Colin Coupe, Adrian Brown and David Brown\u2026 Thanks all!<\/p>\n\n\n\n

Introducing Graphics<\/h1>\n\n\n\n

What are sprites?<\/h2>\n\n\n\n

Wikipedia <\/a>describes sprites as;<\/p>\n\n\n\n

\n

\u2026a two-dimensional<\/a> bitmap<\/a> that is integrated into a larger scene.<\/p>\n<\/blockquote>\n\n\n\n

In simple terms, a sprite is a small piece of graphics on the screen;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

The main sprite in the above scene of course is our egg shaped friend Dizzy\u2026 In fact, there\u2019s quite a few Sprites in the GIF above\u2026 The Cloud, the Spider, the Well, the Cauldron and so on\u2026<\/p>\n\n\n\n

In our example above, some of the sprites are changing their appearance, that is, to give the impression of movement, a series of different sprites are drawn one after another to provide animation. Nonetheless, each frame of the animation is made up of simply one sprite.<\/p>\n\n\n\n

Introducing Graphics<\/h1>\n\n\n\n

What are sprites\u2026 Really?<\/h2>\n\n\n\n

Ok, so Sprites are actually just a collection of bytes of data relating to a set of individual pixels. Here\u2019s an example of a simple sprite;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

So, we have a white square filled with black\u2026 This could be viewed as series of 1\u2019s and 0\u2019s such as;<\/p>\n\n\n\n

1, 1, 1, 1, 1, 1, 1, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 0, 0, 0, 0, 0, 0, 1
1, 1, 1, 1, 1, 1, 1, 1<\/code><\/code><\/pre>\n\n\n\n

Where the 1\u2019s will show a white pixel and the 0\u2019s will show a black pixel. This is of course a simplifcation\u2026 The Sprite above is actually stored in memory as a series of bytes as follows;<\/p>\n\n\n\n

DB &FF,&FF,&FF,&FF
DB &F0,&00,&00,&0F
DB &F0,&00,&00,&0F
DB &F0,&00,&00,&0F
DB &F0,&00,&00,&0F
DB &F0,&00,&00,&0F
DB &F0,&00,&00,&0F
DB &FF,&FF,&FF,&FF<\/code><\/code><\/pre>\n\n\n\n

You\u2019ll see above, that there\u2019s actually half as many columns of bytes above as you\u2019d expect. This is because, to save space, the SAM combines columns of pixels into a single byte, with adjacent columns occupying adjacent nibbles (4 bits) of a byte. So, if we look again, at the second row down, we have;<\/p>\n\n\n\n

DB &F0,&00,&00,&0F<\/code><\/code><\/pre>\n\n\n\n

We can see that the &F<\/code>\u2018s correspond to our 1<\/code>\u2018s and the &0<\/code>\u2018s correspond to our 0<\/code>\u2018s (as you\u2019d expect). And you can see that the first byte is &F0<\/code>, so we have a 1<\/code> and 0<\/code> pixel described in a single byte, with the opposite at the end where we have &0F<\/code>, with a 0<\/code> and a 1<\/code> described in a single byte.<\/p>\n\n\n\n

Introducing Graphics<\/h1>\n\n\n\n

The SAM Coup\u00e9 Palette<\/h2>\n\n\n\n

To take things a little further, if we now consider the following sprite;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

We now have a Green box with black centre\u2026. The code for this sprite is;<\/p>\n\n\n\n

DB &CC,&CC,&CC,&CC
DB &C0,&00,&00,&0C
DB &C0,&00,&00,&0C
DB &C0,&00,&00,&0C
DB &C0,&00,&00,&0C
DB &C0,&00,&00,&0C
DB &C0,&00,&00,&0C
DB &CC,&CC,&CC,&CC<\/code><\/code><\/pre>\n\n\n\n

So, we can now see that our &F<\/code>\u2018s have changed to&C<\/code>\u2018s. This is because the colour of each pixel is denoted by the value in each nibble of each byte. Each nibble can be set to one of 16 colours: 0 to F. The SAM Coup\u00e9 however, actually has access to a total palette of 128 colours, but only 16 can be chosen at any one time. We\u2019ll actually expand upon this somewhat later, where we\u2019ll talk about Screen modes.<\/p>\n\n\n\n

The main SAM Coup\u00e9 Welcome Screen has a good example of the basic SAM Coup\u00e9 Palette;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

Each of the colours in the screen above is assigned to a number;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

I\u2019ve added the first black row here, as I guess MGT didn\u2019t like a black bar at the top. You\u2019ll see that colour 12 or C in HEX, is green and corresponds to the green in our sprite above.<\/p>\n\n\n\n

These 16 colours are the default colours given to you when you first turn on your SAM\u2026 But, the SAM Coup\u00e9 can actually choose from 128 colours in total. The full SAM Coup\u00e9 Palette of 128 colours is shown below, with each colour\u2019s associated number given beside it (Click to open the full image);<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

If you\u2019d like to see the palette on your SAM Coup\u00e9, here\u2019s a disk image<\/a> which will automatically load the little palette test program from page 163 of the SAM Coup\u00e9 User Manual.<\/p>\n\n\n\n

We get to choose each of the 128 colours by using a technique called Palette Switching, which we\u2019ll get onto later\u2026 It\u2019s actually interesting to note that, although the SAM Coup\u00e9 Welcome Screen looks straightforward enough, the Image is actually created using Palette Switching too rather than using a similair technique to the Palette Demo Program.<\/p>\n\n\n\n

Introducing Graphics<\/h1>\n\n\n\n

The Screen Layout<\/h2>\n\n\n\n

The SAM Coup\u00e9 has four different screen modes for different purposes, which are named Modes 1 to 4. They differ in the following ways as described in the SAM Coup\u00e9 User\u2019s Guide;<\/p>\n\n\n\n

\n

MODE 1<\/h3>\n\n\n\n

32 cells * 24 lines = 768 character cells, each cell has individual choice of PEN and PAPER colour.
256 * 192 pixels, choice of any 16 screen colours from 128.<\/p>\n\n\n\n

MODE 2<\/h3>\n\n\n\n

32 cells * 192 lines = 6,144 cells, each cell has individual choice of PEN and PAPER colour.
256 * 192 pixels, choice of any 16 screen colours from 128.<\/p>\n\n\n\n

MODE 3<\/h3>\n\n\n\n

512 pixels * 192 lines, each pixel has individual choice of colour, choice of any 4 screen colours from 128.<\/p>\n\n\n\n

MODE 4<\/h3>\n\n\n\n

256 pixels * 192 lines, each pixel has individual choice of colour, choice of any 16 screen colours from 128.<\/p>\n<\/blockquote>\n\n\n\n

It\u2019s amusing to note that, the above section of the SAM Coup\u00e9 User Guide (page 66) has an error, which states that that Mode 2\u2019s 32\u00d7192 characters = 5,444 rather than the correct 6,144!<\/p>\n\n\n\n

The default mode that the SAM Coup\u00e9 starts in is Mode 4, and this is the mode we\u2019ll be using in this guide.<\/p>\n\n\n\n

Aside from Mode 3, you\u2019ll notice that the SAM Coup\u00e9 has a screen resolution of 256 pixels wide by 192 pixels high;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

The origin, or the pixel at location 0 down by 0 across is in the top left-hand corner of the screen.<\/p>\n\n\n\n

About Memory<\/h1>\n\n\n\n

The SAM Coup\u00e9 Memory Map<\/h2>\n\n\n\n

Much like a lot of the 8 Bit computers from the 80\u2019s and 90\u2019s, the SAM Coup\u00e9 uses a Z80B 8 Bit processor. To coordinate all of this MGT designed an ASIC, or Application Specific Integrated Circuit. The ASIC contains the control of the various SAM functions and Hardware\u2026 Everything from controlling the Display to controlling the on board MIDI ports.<\/p>\n\n\n\n

The Z80B can only access 64 KB of Memory at any given time, which is in turn broken down into 4 banks of 16KB. This limitation of course is part an parcel of the Z80B being an 8 Bit Microprocessor, with a 16bit address space\u2026 Where, the maximum number which can be represented by 16 Bits is 65535 (or 65536 if you count 0).<\/p>\n\n\n\n

But, we know of course, that the SAM Coup\u00e9 has upto 512KB of RAM, so how does this 512KB translate to the 64KB addressable by the Z80B? One of the main functions of the ASIC is controlling how the Z80B communicates with the SAM Coup\u00e9\u2019s Memory, both ROM (Read Only Memory) and RAM (Random Access Memory);<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

This is a simplified view of how things are connected together of course\u2026 But, we can work with this to help explain how things hang together.<\/p>\n\n\n\n

About Memory<\/h1>\n\n\n\n

Paging<\/h2>\n\n\n\n

So, we know that the Z80B can only address up to 64KB of Memory, but we have up to 512KB to play with. Here\u2019s where some of the magic of the SAM Coup\u00e9 ASIC comes to life. In order to allow the Z80B to see our RAM (and ROM), that is, in order for the Z80B to execute instructions and process data held in our RAM, the ASIC \u201cPages\u201d sections into the relevant positions in the 64KB address space that the Z80B can see it.<\/p>\n\n\n\n

Wikipedia <\/a>describes Memory Paging as;<\/p>\n\n\n\n

\n

 \u2026a memory management<\/a> scheme by which a computer stores and retrieves data from secondary storage<\/a><\/sup> for use in main memory<\/a><\/p>\n<\/blockquote>\n\n\n\n

Pretty obvious right? Well, in basic terms, the ASIC takes up to the full 512KB of the SAM Coup\u00e9\u2019s RAM and splits it up into 32 Page<\/strong>s of 16KB. This then allows us to choose which sections of memory the Z80B can see at any one time. We choose which Pages the Z80B can see by using the LMPR and HMPR registers;<\/p>\n\n\n\n

LMPR:<\/strong><\/h3>\n\n\n\n

This register primarily controls which pages of RAM are paged into Sections A and B of the Z80B Memory space.<\/p>\n\n\n\n

Bit<\/th>Name<\/th>Function<\/th><\/tr>
0<\/td>BCD 1<\/td>Low Memory Page Control<\/td><\/tr>
1<\/td>BCD 2<\/td>Low Memory Page Control<\/td><\/tr>
2<\/td>BCD 4<\/td>Low Memory Page Control<\/td><\/tr>
3<\/td>BCD 8<\/td>Low Memory Page Control<\/td><\/tr>
4<\/td>BCD 16<\/td>Low Memory Page Control<\/td><\/tr>
5<\/td>RAM0<\/td>If set, ROM 0 is Paged out Section A of
Memory and RAM paged in its place<\/td><\/tr>
6<\/td>RAM1<\/td>If set, ROM 1 is Paged into Section D of Memory<\/td><\/tr>
7<\/td>WPRAM<\/td>If set, will write protect the RAM in Section A<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

HMPR:<\/strong><\/h3>\n\n\n\n

This register primarily controls which pages of RAM are paged into Sections C and D of the Z80B Memory space.<\/p>\n\n\n\n

Bit<\/th>Name<\/th>Function<\/th><\/tr>
0<\/td>BCD 1<\/td>High Memory Page Control<\/td><\/tr>
1<\/td>BCD 2<\/td>High Memory Page Control<\/td><\/tr>
2<\/td>BCD 4<\/td>High Memory Page Control<\/td><\/tr>
3<\/td>BCD 8<\/td>High Memory Page Control<\/td><\/tr>
4<\/td>BCD 16<\/td>High Memory Page Control<\/td><\/tr>
5<\/td>MD3S0<\/td>BCD 4 of the Mode 3 Colour Lookup Address<\/td><\/tr>
6<\/td>MD3S1<\/td>BCD 8 of the Mode 3 Colour Lookup Address<\/td><\/tr>
7<\/td>MCNTRL<\/td>Set to Allow access to the External Memory Space<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

&nbsp<\/p>\n\n\n\n

About Memory<\/h1>\n\n\n\n

How Paging Works<\/h2>\n\n\n\n

If we look back at our simplified view of how the SAM Coup\u00e9 Memory is organised, then we can see that the ASIC allows us, through the use of the LMPR, HMPR and VMPR registers, to allow the Z80 to read and write our RAM. The best way to imagine this is like a set of circuits perhaps, where we connect a wire between what the Z80B can read and write, and our RAM. So, imagine if we want to read and write the first four pages of our RAM\u2026 We\u2019d need to tell the ASIC to page these into Sections A,B,C and D of the address space of the Z80B. Which is something like this graphically;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

This actually works by setting the LMPR and HMPR registers up as follows;<\/p>\n\n\n\n

LMPR:<\/h3>\n\n\n\n
Bit<\/th>7<\/th>6<\/th>5<\/th>4<\/th>3<\/th>2<\/th>1<\/th>0<\/th><\/tr>
Value<\/td>0<\/td>0<\/td>1<\/td>0<\/td>0<\/td>0<\/td>0<\/td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

HMPR:<\/h3>\n\n\n\n
Bit<\/th>7<\/th>6<\/th>5<\/th>4<\/th>3<\/th>2<\/th>1<\/th>0<\/th><\/tr>
Value<\/td>0<\/td>0<\/td>0<\/td>0<\/td>0<\/td>0<\/td>1<\/td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

You\u2019ll notice in both cases that we only tell the ASIC to point to the first Page in memory\u2026 That is, we only tell the ASIC to put Page 0 in Section A and Page 2 in Section C. The ASIC will then take the next logical bank and page this automatically into the next section. So, we\u2019ve put Page 0 into Section A and the ASIC automatically puts Page 1 into Section B. We put Page 1 into Section C and the ASIC automatically puts Page 3 into Section D.<\/p>\n\n\n\n

About Memory<\/h1>\n\n\n\n

Screen Memory<\/h2>\n\n\n\n

So, we\u2019ve explored how we go about reading and writing the SAM Coup\u00e9\u2019s RAM (and ROM). If we\u2019re going to put anything on the screen, then we\u2019re going to need to write to the Screen Memory.<\/p>\n\n\n\n

The SAM Coup\u00e9 uses the VMPR register to let the ASIC know where it should look for the information to display on the Screen. It\u2019s actually the SAM Coup\u00e9\u2019s ASIC which deals with displaying data on the screen, and doesn\u2019t involve the Z80B in this process at all.<\/p>\n\n\n\n

The Screen Memory is slightly different to the regular RAM access, in that, for the ASIC to access the information to display on the screen, the ASIC simply needs to be instructed as to where in the full 512KB (or 256KB) the data resides. Once the VMPR is setup, the ASIC can continue to update the screen from the RAM without the need to use Paging to do so;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n

The VMPR register is as follows;<\/p>\n\n\n\n

VMPR:<\/strong><\/h3>\n\n\n\n

This register primarily controls which pages of RAM are Allocated to the Screen Memory.<\/p>\n\n\n\n

Bit<\/th>Name<\/th>Function<\/th><\/tr>
0<\/td>BCD 1<\/td>Video Memory Page Control<\/td><\/tr>
1<\/td>BCD 2<\/td>Video Memory Page Control<\/td><\/tr>
2<\/td>BCD 4<\/td>Video Memory Page Control<\/td><\/tr>
3<\/td>BCD 8<\/td>Video Memory Page Control<\/td><\/tr>
4<\/td>BCD 16<\/td>Video Memory Page Control<\/td><\/tr>
5<\/td>MDEO<\/td>Screen Mode Control \u2013 Bit 1<\/td><\/tr>
6<\/td>MDE1<\/td>Screen Mode Control \u2013 Bit 2<\/td><\/tr>
7<\/td>TXMIDI<\/td>MIDI Output Enable Bit<\/td><\/tr>
7<\/td>RXMIDI<\/td>MIDI IN Bit<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

You can see from the table about that we only need to specify a single 16KB page of RAM where our screen is located. However, in Modes 3 and 4, the screen actually consumes 24KB of RAM. The ASIC handles this by automatically assigning the next page, the page directly after the one specified in the VMPR, as screen memory also.<\/p>\n\n\n\n

It\u2019s worth noting that the names of the modes follow the BASIC naming convention, i.e. Modes 1,2,3 and 4. However, to select Mode 1, we actually set the Mode Control Bits to 0 of course.<\/p>\n\n\n\n

In Mode 4, if we wanted to use Pages 28 and 29 of RAM, as in the diagram above, as Screen Memory, we\u2019d set the VMPR up as follows;<\/p>\n\n\n\n

VMPR:<\/h3>\n\n\n\n
Bit<\/th>7<\/th>6<\/th>5<\/th>4<\/th>3<\/th>2<\/th>1<\/th>0<\/th><\/tr>
Value<\/td>0<\/td>1<\/td>1<\/td>1<\/td>1<\/td>1<\/td>0<\/td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n

So, we\u2019ve set the Page Bits (Bits 0-4) to Point to Page 28, and the Page Mode Bits (Bits 5 & 6), as Page Mode 4.<\/p>\n\n\n\n

When it comes time for us to actually modify the screen memory, we still need to page the relevant pages into the Z80B\u2019s 64KB Address Space, just as with any other RAM operation, however as mentioned above, the ASIC automatically handles allowing the screen to be updated from the RAM directly.<\/p>\n\n\n\n

A Basic Square Sprite<\/h1>\n\n\n\n

The Code<\/h2>\n\n\n\n

So, to get us going, let\u2019s just get some really basic pixels drawn on the screen shall we\u2026<\/p>\n\n\n\n

Below is some basic code which will display a very simple square sprite like the following;<\/p>\n\n\n

\n
\"\"<\/a><\/figure><\/div>\n\n\n
<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>
;<\/span><\/span>\n; -----------------------------------------------------------------------------------<\/span><\/span>\n; |                                                                                 |<\/span><\/span>\n; | Program:    SAM Coupe Sprites Tutorial 1 - Part 0                               |<\/span><\/span>\n; | Filename:   Sprites0.asm                                                        |<\/span><\/span>\n; | Version:    1.0                                                                 |<\/span><\/span>\n; | Date:       12\/10\/2017                                                          |<\/span><\/span>\n; | Author:     Pete Gallagher - PJG Creations Ltd                                  |<\/span><\/span>\n; |                                                                                 |<\/span><\/span>\n; -----------------------------------------------------------------------------------<\/span><\/span>\n;<\/span><\/span>\n                autoexec\t\t<\/span>; Automatically Execute the Program<\/span><\/span>\n\t\tORG \t<\/span>32768<\/span>\t\t<\/span>; Store the program in User Address Space<\/span><\/span>\n;<\/span><\/span>\n; Our Equates<\/span><\/span>\n;<\/span><\/span>\nLMPR:<\/span>           <\/span>EQU<\/span>     <\/span>250<\/span>             <\/span>; The Lower Memory Page Register Address <\/span><\/span>\nHMPR:<\/span>           <\/span>EQU<\/span>     <\/span>251<\/span>             <\/span>; The Higher Memory Page Register Address <\/span><\/span>\nVMPR:<\/span>           <\/span>EQU<\/span>     <\/span>252<\/span>             <\/span>; The Video Memory Page Register Address <\/span><\/span>\n;<\/span><\/span>\n; Get the Current Screen Page and set the LMPR register up so we can write to the screen<\/span><\/span>\n;<\/span><\/span>\n                <\/span>IN<\/span>      A,(VMPR)        <\/span>; Get the VMPR Register, which holds info about...<\/span><\/span>\n                                        <\/span>; ... which Page the Screen is Paged Into<\/span><\/span>\n                <\/span>AND<\/span>     <\/span>0B00011110<\/span>      <\/span>; We're only interested in the Screen Page, so mask off everything but the Page Bits...<\/span><\/span>\n                                        <\/span>; ... The result will be stored in the A Register<\/span><\/span>\n                <\/span>OR<\/span>      <\/span>0B00100000<\/span>      <\/span>; Set Bit 5 which, when loaded into the LMPR register, will Set the RAM in Page 0<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR),A        <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; Print a sprite in the top left corner<\/span><\/span>\n;<\/span><\/span>\n                LD      HL,<\/span>0x0000<\/span>       <\/span>; Point to the Top Left corner of our screen<\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0080<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0001<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0081<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0100<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0180<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0101<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of this pixel<\/span><\/span>\n<\/span>\n                LD      HL,<\/span>0x0181<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of this pixel                <\/span><\/span>\n;<\/span><\/span>\n; We must remember to switch BASIC back into Page 0<\/span><\/span>\n;<\/span><\/span>\n                LD      A,<\/span>0x1f<\/span>          <\/span>;<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR), A       <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; Exit back to BASIC<\/span><\/span>\n;<\/span><\/span>\n                <\/span>RET<\/span>                     <\/span>;   <\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

A Basic Square Sprite<\/h1>\n\n\n\n

Understanding the Code<\/h2>\n\n\n\n

Setup and Equates:<\/h3>\n\n\n\n

As in the previous tutorial, we need to tell the Assembler where to put our code in the SAM Coup\u00e9 RAM, and we also would like our code to Automatically Execute. We set this up on lines 12 and 13;<\/p>\n\n\n\n

autoexec <\/strong><\/code>\nORG<\/strong> 32768<\/code><\/code><\/pre>\n\n\n\n

Next we make our lives slightly easier when dealing with the LMPR, HMPR and VMPR, by creating what\u2019s known as an equate. This basically replaces a number with a nice friendly label. So we have the following at lines 17 to 19;<\/p>\n\n\n\n

LMPR: EQU<\/strong> 250\nHMPR: EQU <\/strong>251\nVMPR: EQU <\/strong>252<\/code><\/pre>\n\n\n\n

Where 250 is the address of the LMPR, 251 is the HMPR and 252 is the VMPR.<\/p>\n\n\n\n

Getting the Screen RAM location:<\/h3>\n\n\n\n

As explained above, The SAM Coup\u00e9 can read and display the information from RAM, however if we want to write to the Screen Data, we need to page this into the Z80B address space.<\/p>\n\n\n\n

The SAM Coup\u00e9 starts up with the VMPR setup to point to Pages 30 and 31. But, to write reusuable code, we need to actually find the page of RAM that the VMPR is pointing to, and load this address into our LMPR so that we can write to it. The section on lines 23 to 28 achieve just this;<\/p>\n\n\n\n

IN<\/strong>   A,(VMPR)\nAND<\/strong>  0B00011110\nOR<\/strong>   0B00100000\nOUT<\/strong>  (LMPR),A<\/code><\/pre>\n\n\n\n

First, we grab the current location of Screen Memory from the VMPR using indirect addressing (denoted by the brackets).<\/p>\n\n\n\n

Next we use an AND statement to remove everything except for the Screen Page bits (bits 0-4)\u2026 We also ignore bit 0, as the Screen Memory is only loaded in at even locations, as if we\u2019re in Modes 3 or 4, then the ASIC will automatically assign the subsequent page to fulfil the 24KB requirement mentioned above.<\/p>\n\n\n\n

After this we set bit 5 of the A register, which when loaded into the LMPR register, will page the ROM our of Page 0, and page RAM in in its place.<\/p>\n\n\n\n

Finally, we load our A register into our LMPR, once again using indirect addressing.<\/p>\n\n\n\n

Writing to the screen:<\/h3>\n\n\n\n

Ok, so now we\u2019ve got the screen memory paged into the Z80B\u2019s address space, and we\u2019re ready to start writing some information to display!<\/p>\n\n\n\n

With our Screen Memory paged into Sections A & B, then the top left hand corner of the screen will be at address 0. So, on line 32, we set the HL register pair up to point to address 0;<\/p>\n\n\n\n

LD<\/strong> HL,0x0000<\/code><\/code><\/pre>\n\n\n\n

With the HL register pair set up, we can now write our first pixel to screen memory. In actual fact, we\u2019ll be writing two pixels at once, as explained in the \u201cIntroducing Graphics \u2013 The SAM Coup\u00e9 Palette\u201d section above.<\/p>\n\n\n\n

In order to maximise the RAM usage, each Screen Memory location is split into two halves, with adjacent pixels occupying the Upper and Lower Nibbles of each Byte. Working from Left to Right, the Upper Nibble of a Byte (bits 4-7), represent the left most pixel at a location, and the Lower Nibble (bits 0-3), represent the right most pixel at a location.<\/p>\n\n\n\n

So, on line 33, we load a value of 0xFF into the location Pointed to by the HL register \u2013 Address 0 or the top left corner of our screen. This in turn sets the pixel at location 0,0 (row 0, line 0) to White, aswell as the pixel at location 1,0 (row 1, line 0);<\/p>\n\n\n\n

LD<\/strong> (HL),0xFF<\/code><\/code><\/pre>\n\n\n\n

This should have the effect of drawing two white pixels at the top left corner of the screen.<\/p>\n\n\n\n

Next we want to draw to green pixels directly below our two white pixels. We know from the specifications that the SAM Coup\u00e9 Screen (in mode 4), is 256 pixels wide. So, each line is 256 Pixels long. So, in order to write a pixel on the second line down, we simply need to add 256 (0x80 in HEX) to the value in our HL register pair, and we move a line down. We can see this on line 35;<\/p>\n\n\n\n

LD<\/strong> HL,0x0080<\/code><\/code><\/pre>\n\n\n\n

We\u2019re now pointing at 0,1 (row 0, line 1), directly under our first two white pixels. As explained above, the colour information of our pixels is denoted by the value loaded into each nibble of the Screen Memory Bytes. We used 0xF to print a white pixel, we\u2019ll now write a green pixel on line 36, so we can see the difference between each line;<\/p>\n\n\n\n

LD<\/strong> (HL),0x44<\/code><\/code><\/pre>\n\n\n\n

We then repeat this action 4 more times on lines 38 to 54, writing two pixels at a time on alternate lines until we complete our first sprite.<\/p>\n\n\n\n

Returning us safely to BASIC:<\/h3>\n\n\n\n

Before our program exits, it\u2019s always a good idea to return the ROM back to Sections A and B, as BASIC requires this when we return to it. We accomplish this on lines 58 & 59, by simply writing a value of 0x1F back to our LMPR, which pages in the RAM at page 31, as well as clearing the bit which returns ROM0 to Section A;<\/p>\n\n\n\n

LD<\/strong>  A,0x1f<\/code>\nOUT<\/strong> (LMPR), A<\/code><\/code><\/pre>\n\n\n\n

Finally, we return back to BASIC using the RET command on line 63, which exits our program, returning us to the friendly home comforts of BASIC.<\/p>\n\n\n\n

A Basic Square Sprite<\/h1>\n\n\n\n

Slowing the process down<\/h2>\n\n\n\n

To give you a better idea of how the sprite is drawn onto the screen, we can add a routine between each pixel such that the process will wait for the user to press a key;<\/p>\n\n\n\n

<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>
;<\/span><\/span>\n; -----------------------------------------------------------------------------------<\/span><\/span>\n; |                                                                                 |<\/span><\/span>\n; | Program:    SAM Coupe Sprites Tutorial 1 - Part 1                               |<\/span><\/span>\n; | Filename:   Sprites1.asm                                                        |<\/span><\/span>\n; | Version:    1.0                                                                 |<\/span><\/span>\n; | Date:       12\/10\/2017                                                          |<\/span><\/span>\n; | Author:     Pete Gallagher - PJG Creations Ltd                                  |<\/span><\/span>\n; |                                                                                 |<\/span><\/span>\n; -----------------------------------------------------------------------------------<\/span><\/span>\n;<\/span><\/span>\n\t\tautoexec\t\t<\/span>; Automatically Execute the Program<\/span><\/span>\n\t\tORG \t<\/span>0x8000<\/span>\t\t<\/span>; Store the program in User Address Space<\/span><\/span>\n;<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; *                                    Equates                                      *<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n;<\/span><\/span>\nLMPR:<\/span>           <\/span>EQU<\/span>     <\/span>250<\/span>             <\/span>; The Lower Memory Page Register Address <\/span><\/span>\nHMPR:<\/span>           <\/span>EQU<\/span>     <\/span>251<\/span>             <\/span>; The Higher Memory Page Register Address <\/span><\/span>\nVMPR:<\/span>           <\/span>EQU<\/span>     <\/span>252<\/span>             <\/span>; The Video Memory Page Register Address <\/span><\/span>\nJCLSBL:<\/span>         <\/span>EQU<\/span>     <\/span>334<\/span>             <\/span>; The Clear Screen Routine (Set A to 0 to clear the whole screen) (0x14E)<\/span><\/span>\n;<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; *                                Application Start                                *<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n;<\/span><\/span>\n                <\/span>CALL<\/span>    Clear_Screen    <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; Get the Current Screen Page and set the LMPR register up so we can write to the screen<\/span><\/span>\n;<\/span><\/span>\n                <\/span>IN<\/span>      A,(VMPR)        <\/span>; Get the VMPR Register, which holds info about...<\/span><\/span>\n                                        <\/span>; ... which Page the Screen is Paged Into<\/span><\/span>\n                <\/span>AND<\/span>     <\/span>0B00011110<\/span>      <\/span>; We're only interested in the Screen Page, so mask off everything but the Page Bits...<\/span><\/span>\n                                        <\/span>; ... The result will be stored in the A Register<\/span><\/span>\n                <\/span>OR<\/span>      <\/span>0B00100000<\/span>      <\/span>; Set Bit 5 which, when loaded into the LMPR register, will Set the RAM in Page 0<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR),A        <\/span>;<\/span><\/span>\n;<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; Print a sprite in the top left corner<\/span><\/span>\n;<\/span><\/span>\n                LD      HL,<\/span>0x0000<\/span>       <\/span>; Point to the Top Left corner of our screen<\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of pixels 0,0 and 0,1 to White               <\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0080<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of pixels 1,0 and 1,1 to Green<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0001<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of pixels 0,2 and 0,3 to White<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0081<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of pixels 1,2 and 1,3 to Green<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0100<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of pixels 1,0 and 1,1 to White<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0180<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of pixels 2,0 and 2,1 to Green<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0101<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0xFF<\/span>       <\/span>; Set the colour of pixels 1,2 and 1,3 to White<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;<\/span><\/span>\n                LD      HL,<\/span>0x0181<\/span>       <\/span>; <\/span><\/span>\n                LD      (HL),<\/span>0x44<\/span>       <\/span>; Set the colour of pixels 2,2 and 2,3 to Green<\/span><\/span>\n                <\/span>CALL<\/span>    Wait_For_Key    <\/span>;               <\/span><\/span>\n;<\/span><\/span>\n; We must remember to switch BASIC back into Page 0<\/span><\/span>\n;<\/span><\/span>\n                LD      A,<\/span>0x1F<\/span>          <\/span>;<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR), A       <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; Exit back to BASIC<\/span><\/span>\n;<\/span><\/span>\n                <\/span>RET<\/span>                     <\/span>;<\/span><\/span>\n;<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; *                                Clear the Screen                                 *<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n;<\/span><\/span>\nClear_Screen:<\/span>   LD      A,<\/span>0<\/span>             <\/span>;<\/span><\/span>\n                <\/span>CALL<\/span>    JCLSBL          <\/span>;<\/span><\/span>\n                <\/span>RET<\/span><\/span>\n;<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; *                                Wait for a KeyPres                               *<\/span><\/span>\n; *                                                                                 *<\/span><\/span>\n; ***********************************************************************************<\/span><\/span>\n;<\/span><\/span>\nWait_For_Key:<\/span>   <\/span>IN<\/span>      A, (LMPR)       <\/span>;<\/span><\/span>\n                RES     <\/span>5<\/span>, A            <\/span>;<\/span><\/span>\n                SET     <\/span>0<\/span>, A            <\/span>;<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR),A<\/span><\/span>\n;<\/span><\/span>\nKey_Loop:<\/span>       <\/span>CALL<\/span>\t&<\/span>0169<\/span>\t\t<\/span>; Read the Keyboard (Zero if no key)<\/span><\/span>\n       \t\tJR \tZ,Key_Loop      <\/span>; If no key pressed, then loop<\/span><\/span>\n;<\/span><\/span>\nNo_Key_Loop:<\/span>    <\/span>CALL<\/span>\t&<\/span>0169<\/span>\t\t<\/span>; Read the Keyboard (Zero if no key)<\/span><\/span>\n       \t\tJR \tNZ,No_Key_Loop  <\/span>; If key still pressed, then loop<\/span><\/span>\n\t\t\t\t<\/span><\/span>\n                <\/span>IN<\/span>      A,(LMPR)        <\/span>;<\/span><\/span>\n                SET     <\/span>5<\/span>, A            <\/span>;<\/span><\/span>\n                RES     <\/span>0<\/span>, A            <\/span>;<\/span><\/span>\n                <\/span>OUT<\/span>     (LMPR),A<\/span><\/span>\n;<\/span><\/span>\n                <\/span>RET<\/span>                     <\/span>;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

The Clear Screen ROM Routine:<\/h3>\n\n\n\n

Most of the first section of our new Part 1 code above is the same as in Part 0, until we get to lines 24 and 32 where we\u2019re defined an equate for the JCLSBL<\/strong> Clear Screen ROM Routine on line 24;<\/p>\n\n\n\n

EQU<\/strong> 334<\/code><\/code><\/pre>\n\n\n\n

We then call a little subroutine of our own on line 32 which sets up the A<\/strong> Register and calls the ROM routine.<\/p>\n\n\n\n

The JCLSBL <\/strong>routine either clears the whole screen, if the A<\/strong> register is 0, or just the lower part of the screen if the A<\/strong> register isn\u2019t 0. It\u2019s counterpart JCLSLOWER<\/strong> clears the bottom half of the screen.<\/p>\n\n\n\n

This routine forms a set of ROM routines which include, but are not limited to;<\/p>\n\n\n\n

You can download a disk Image of the above code here<\/a>.<\/p>\n\n\n\n