📄 exa1.html
字号:
<!doctype html public "-//W3C//DTD HTML 3.2//EN"><html><head><title>Example 1: Screen image compressor</title><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /><meta http-equiv="content-language" content="en" /><meta name="resource-type" content="document"><meta name="copyright" content="This document copyright 2001 by Richard Murray. Use for non-profit and education purposes explicitly granted."><meta name="author" content="Richard Murray"><meta name="rating" content="general"></head><!-- /assembler/exa1.html --><!-- --><!-- (C) Copyright 1999 Richard Murray --><!-- Designed by Richard Murray --><!-- rmurray@heyrick.co.uk --><!-- --><body bgcolor="#f0f0f0" text="#000000" link="#0022dd" vlink="#002288"><table border = "0" width="100%"> <tr> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td> <td> <h1 align="center"><font color="#800080">Example 1<br>Screen image compressor</font></h1> </td> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td></table><p> <p>What we shall do here is to jump straight in with an example program. Something that you willfind useful (I mean - what <i>is</i> the point of writing "Hello World" a few thousandtimes? When you learn to code it is much better to learn on <i>useful</i> projects).<p>This project is a 16-colour screen mode compressor. It can be adapted to other modes, but I'llleave that as an exercise for the advanced. After all - ChangeFSI does a marvellous job ofreducing the number of colours in an image, and for demos with 'real life screenshots', 16colours is usually adequate (unless you are plugging the latest 16 million colour game you wrote- in which case you won't need this utility!).<p>Average savings depend on the image. It does badly with dithered images, it does well withimages that don't have loads of fiddly things (a screenshot of a program running in an <i>untextured</i> Desktop, for example). It has been designed to work with MODE 12 and MODE 35.For others, it is a case of suck it and see - but there is no real reason why other 16 colourMODEs would not work.<p>Right. Well... In the old days when it was safe to leave your car unlocked and your daughter unchained, screen memory started at &2000000 and worked upwards. You needed to <i>know</i> a fewmodes such as 12 and 13 and 15... Like, who do you know that uses MODE 8?<p>Modern life, however, is not so simple. We would be foolish to assume the screen start position and/or size. We would also be foolish to assume the 'default' palette. So all of this information has to be examined.<br>16 colour modes use 4bpp, or 4 bits per pixel. Thus, one byte equals two pixels.<p>The compression system is appropriately crude. We will work on a "better" scheme later.However for now we shall be using a kind of Run-Length-Encoding. We treat the screen memory as asequence of bytes, and we encode it into a set of byte counts and byte values. For example,assume screen memory is:<br><center><code>111111111142351111121111111222222222222222111111111111</code></center><br>This would be a bunch of 1's followed by a 4, a 2, a 3, a 5, more ones... Which becomes:<br><center><code>A114121315511271F2C1</code></center><br>This has reduced the stream to approximately a third of original size. The "A1" means10 x 1 (A is 10 in base 16). Then 1 x 4, 1 x 2, 1 x 3, 1 x 5, 5 x 1, 1 x 2, 7 x 1, 15 x 2 andfinally 12 x 1.<p>If you don't get it, study the above for a few moments. It is quite simple once you understand.By way of example, <code>11112246885788</code> becomes <code>4122141628151728</code>; and you cansee how it becomes inefficient with oft changing bytes (such as dithered images), the output istwo bytes larger than the input.<p> <p> <p>Our format is to be defined as:<br><pre> Header: "BudgieScrn (compressed 1)"+<newline> which is 26 bytes. Mode: 1 byte giving mode number. Palette: 16 entries, six bytes each. Giving red, green and blue values for first flash colour and second flash colour for each entry. Image: Data as defined above.</pre>We will leave the screen size validation up to the loader instead of encoding it into the image.<p>So... Let us begin:<pre>REM >Ssaver BudgieSoft screen saverREM V1.01 © 1997 Richard MurrayREMREM Assembler programming example 1REM Downloaded from: http://www.heyrick.co.uk/assembler/:ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END:DIM code% &400FOR pass% = 4 TO 6 STEP 2 P%=0 O%=code% [ OPT pass%</pre>This is a fairly standard beginning. The thing to note is that we are using offset assembly. Inthis way, our code is compiled as a transient utility instead of an "absolute"application. It should also be loadable.The divide-by-ten in the error message is so that 'real lines' match up to !Edit lines, so ifyou get an error just press F5 and enter that number.<pre> MOV R7, R14</pre>We now 'save' the return address in register 7. This is so that it does not get overwritten byBL (Branch with Loop) stuff. Note, if you want to fiddle with the code - remember register 7 is<i>special</i>.<pre> MOV R0, #&8C SWI "XOS_Find" BVS exit MOV R6, R0</pre>Our program is designed to be called as "SSaver <filename>". Conveniently theOS_Find call (opens/closes files) has a flag to look for the filename in the entry parameters.So this stuff attempts to open our file with write access. If it fails the V flag (oVerflow) isset. The BVS command (Branch if V Set) will carry us out to a safe exit. Otherwise we can assumeall went well, and move our file handle into register 6 for safe keeping.<p>By now we have our file open and are ready to start building up our image data.<pre> MOV R0, #2 MOV R1, R6 ADR R2, header_text MOV R3, #26 SWI "XOS_GBPB" BVS error</pre>This sets up a call to OS_GBPB to write the header; which is defined at the end as".header_text". The BVS takes us to an error handler in case we don't havewrite-access after all! <pre> MOV R0, #&87 SWI "XOS_Byte" MOV R0, R2 MOV R1, R6 BL write_byte</pre>If you were to look up OS_Byte &87 in a manual, you might wonder about my sanity. After all -what relevance could "get character at cursor position" have?<p>Frankly - none. :-) However in register 2 it returns the current MODE number, and THAT is whatwe are looking for. We then set up a call to a routine that outputs a byte for us, defined lateras ".write_byte".<pre> MOV R5, #0 .col_loop MOV R0, R5 MOV R1, #16 SWI "OS_ReadPalette" MOV R1, R6 MOV R0, R2, LSR #8 ; red 1st BL write_byte MOV R0, R2, LSR #16 ; green 1st BL write_byte MOV R0, R2, LSR #24 ; blue 1st BL write_byte MOV R0, R3, LSR #8 ; red 2nd BL write_byte MOV R0, R3, LSR #16 ; green 2nd BL write_byte MOV R0, R3, LSR #24 ; blue 2nd BL write_byte ADD R5, R5, #1 CMP R5, #16 BLT col_loop</pre>This looks complicated and I'm not going to explain it to verbosely. However what we are tryingto do here is to read the palette (care of OS_ReadPalette). It returns two registers for the sixcolour values. As we are using 32 bit words, we can cram several 8 bit values into one word.Hence the LSR, which is a Logical Shift Right. LSR takes a value (say %110110100) and shifts it(say two places) to produce an output (such as %001101101). We loop through the 16 colours andextract the six values for each, writing them to the file as we are going along.<p>The header is now complete.<pre> ADR R0, vdu_block ADD R1, R0, #12 SWI "OS_ReadVduVariables" LDR R2, [R1] LDR R3, [R1, #4] ADD R3, R3, R2 SWI "XOS_RemoveCursors"</pre>Even though we are not going to write the screen dimensions to the file; we do need to knowwhere the screen starts and ends. This calls "OS_ReadVduVariables" asking for thescreen base address and the size of the screen. Adding the two gives us our result, with thedata held in the memory area defined later as ".vdu_block". While we are at it, weswitch the cursor off.<pre> MOV R1, R6 .main_loop LDRB R0, [R2] BL write_byte MOV R4, #1 .loop2 LDRB R5, [R2, #1]! ADD R4, R4, #1 CMP R5, R0 BNE skip CMP R4, #&FF BLT loop2 .skip SUB R0, R4, #1 BL write_byte CMP R2, R3 BLT main_loop</pre>Here is the main loop. What it does is it outputs the value of the current byte (ie: the colour)and then it reads through the memory looking to see how long it continues for. As a byte canonly hold 256 different values, it loops up to a maximum of 255. Once a value has beenestablished, it will write it out to the file and then branch back to the main loop; repeatinguntil the end of screen memory is reached.<pre> MOV R0, #&20 BL write_byte MOV R0, #&20 BL write_byte</pre>A really early version of this software messed up by trying to read a little beyond the end ofthe screen (and hence the end of the file), so it became practice to add two spaces to the endof the file - just in case - even though the <cough>bug</cough> was fixed.<pre> MOV R0, #0 MOV R1, R6 SWI "XOS_Find" SWI "XOS_RestoreCursors"</pre>This wraps up, closing our file and restoring the cursor. It falls through to ".exit"which follows next.<pre> .exit MOV PC, R7</pre>Leave the program - places stored return address (in register 7) into the Program Counter(register 15).<pre> .write_byte SWI "XOS_BPut" MOVVC PC, R14</pre>Simple routine to write a byte of data. Not terribly efficient, however you could change it tomove the file handle here instead of beforehand (using MOV R1, R6). It returns by way offunction 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"...<pre> .error MOV R2, R0 MOV R1, R6 MOV R0, #0 SWI "XOS_Find" SWI "XOS_RestoreCursors" MOV R0, R2 ORR PC, R7, #1<<28</pre>This closes the file, restores the cursor and exits with the V flag set (error condition). It isthe 'standard' error handler for this program.<br><font color="red">This is not 32-bit compliant.</font><pre> .vdu_block EQUD 149 EQUD 7 EQUD -1 EQUD 0 EQUD 0</pre>This is the block for the OS_ReadVduVariables command. The 149 and 7 are the codes for thevalues we wish to read. The -1 terminates the list. The two 0's are where the information isplaced by the OS.<pre> .header_text EQUS "BudgieScrn (compressed 1)" EQUB 10 EQUB 0 ALIGN</pre>This is the header.<pre> ]NEXT:OSCLI("Save <Obey$Dir>.Saver "+STR$~(code%)+" "+STR$~(O%))OSCLI("SetType <Obey$Dir>.Saver &FFC"):END</pre>And finally the denouement. Close the loop, save the file and exit.<p>There you have it!<p>To use it, try an Obey file similar to:<pre>| Obey file to run SSaver|ScreenLoad <Obey$Dir>.mypiccy|<Obey$Dir>.SSaver <Obey$Dir>.comp_piccy</pre>In the next section, we shall create a loader. Before looking at that, why not see if you canfiddle with the above to make it work in reverse?<div align = right><a href="sw/ssaver.basic"><i>Download example: ssaver.basic</i></a></div><p><hr size = "3"><a href="index.html#15">Return to assembler index</a><hr size = "3"><address>Copyright © 1999 Richard Murray</address></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -