Example 2
Screen image decompressor

 

Previously, we created a program to save 16 colour screens in a compressed form. What we shall do this time is present code to reload the image.

REM >Sloader BudgieSoft screen loader
REM V1.01 © 1997 Richard Murray
REM
REM Assembler programming example 2
REM Downloaded from: http://www.heyrick.co.uk/assembler/
:
ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END
:
DIM code% &1000
FOR pass% = 4 TO 6 STEP 2
  P%=0
  O%=code%
  [ OPT pass%
Again, a standard beginning...
    MOV    R10, R14
    MOV    R0, #&4C
    SWI    "XOS_Find"
    BVS    exit
    MOV    R5, R0
Our program is designed to be called as "SLoader <filename>". We preserve the return address and try to open the filename in our entry parameters. If the V (oVerflow) flag is set, something went wrong so we exit. Otherwise we move the file handle into R5 for safe keeping.
    MOV    R8, #0
  .skip_header
    MOV    R1, R5
    BL     read_byte
    ADD    R8, R8, #1
    CMP    R8, #26
    BLT    skip_header
Skip the header text.
    MOV    R0, #&87
    SWI    "XOS_Byte"
Get the current screen mode. If you are wondering about the "get character at cursor position", read the previous example again...
    MOV    R1, R5
    BL     read_byte
    CMP    R0, R2
    SWINE  &100 + 22
    SWINE  "OS_WriteC"
Here we read the screen mode defined and compare it with the current screen mode. If different, we change to the required mode.
    ADR    R0, vdu_block
    ADD    R1, R0, #12
    SWI    "OS_ReadVduVariables"
    LDR    R2, [R1]
    LDR    R3, [R1, #4]
    ADD    R3, R3, R2
    SUB    R2, R2, #1
As the screen addresses can change, they are not written to the file. We have to find them out for ourselves. We do assume that MODE 12 is the same across the entire range of RISC OS computers (multisync users can fudge it with the megamodes module). This calls "OS_ReadVduVariables" asking for the screen base address and the size of the screen. Adding the two gives us our result, with the data held in the memory area defined later as ".vdu_block".
    MOV    R4, #0
    ADR    R7, os_block
  .col_loop
    MOV    R1, R5
    STRB   R4, [R7, #0]
    MOV    R0, #17
    STRB   R0, [R7, #1]
    BL     read_byte
    STRB   R0, [R7, #2]
    BL     read_byte
    STRB   R0, [R7, #3]
    BL     read_byte
    STRB   R0, [R7, #4]
    MOV    R0, #&0C
    MOV    R1, R7
    SWI    "OS_Word"
    MOV    R1, R5
    MOV    R0, #18
    STRB   R0, [R7, #1]
    BL     read_byte
    STRB   R0, [R7, #2]
    BL     read_byte
    STRB   R0, [R7, #3]
    BL     read_byte
    STRB   R0, [R7, #4]
    MOV    R0, #&0C
    MOV    R1, R7
    SWI    "OS_Word"
    ADD    R4, R4, #1
    CMP    R4, #16
    BLT    col_loop
This lengthy section takes an area known as "os_block" (defined later) and reads colour data into it, building up each colour in turn.
    MOV    R0, #0
    MOV    R1, R5
  .main_loop
    BL     read_byte
    MOV    R4, R0
    BL     read_byte
  .loop2
    SUB    R0, R0, #1
    STRB   R4, [R2, #1]!
    CMP    R0, #0
    BGT    loop2
    CMP    R2, R3
    BLT    main_loop
Here is the main loop. What it does is it reads the value of the current byte (ie: the colour) and then it reads how long it continues for. As a byte can only hold 256 different values, it loops up to a maximum of 255 times, writing this byte into memory thus reconstructing the image.
    MOV    R0, #0
    MOV    R1, R5
    SWI    "XOS_Find"
This wraps up, closing our file. It falls through to "exit" which follows...
  .exit
    MOV    PC, R10
Leave the program - places stored return address (in register 10) into the Program Counter (register 15).
  .read_byte
    SWI    "XOS_BGet"
    MOVVC  PC, R14
Simple routine to read a byte of data. Not terribly efficient, however you could change it to move the file handle here instead of beforehand (using MOV R1, R5). It returns by way of function returning (move return address into Program Counter); but only if the V flag is clear. If something went wrong, however, then we fall through to "error" which follows.
  .error
    MOV    R2, R0
    MOV    R1, R5
    MOV    R0, #0
    SWI    "XOS_Find"
    MOV    R0, R2
    ORR    PC, R7, #1<<28
This closes the file, restores the cursor and exits with the V flag set (error condition). It is the 'standard' error handler for this program.
  .vdu_block
    EQUD   149
    EQUD   7
    EQUD   -1
    EQUD   0
    EQUD   0
This is the block for the OS_ReadVduVariables command. The 149 and 7 are the codes for the values we wish to read. The -1 terminates the list. The two 0's are where the information is placed by the OS.
.os_block
  EQUD   0
  EQUB   0
  ALIGN
This reserves a little memory for the OS_Word call which sets up the palette.
  ]
NEXT

OSCLI("Save <Obey$Dir>.Sloader "+STR$~(code%)+" "+STR$~(O%))
OSCLI("SetType <Obey$Dir>.Sloader &FFC")
And finally the end. Close the loop, save the file and exit.

There you have it!

To use it, try an Obey file similar to:

| Obey file to run SLoader
|
<Obey$Dir>.SLoader <Obey$Dir>.comp_piccy

 

 

Now, you should remember that I showed you how this format can be inefficient (in example 1).

So we shall, in the next part, propose a new format that will make the compression even more efficient. And, later, we shall consider a "smart" loader that can automatically identify the type of image being loaded and decode it properly.

Now you have the ability to load and save compressed 16 colour screens. You have done something useful with your ARM code. Wasn't that better than poking "Hello World!" into random memory locations to see how quickly you could crash the computer? :-)

Download example: sloader.basic
Download example compressed screen: screen.raw


Return to assembler index
Copyright © 2001 Richard Murray