In the last part I said, "So we shall, in the next part, propose a new format that will make the compression even more efficient."
So, then, how can we make the compression more efficient? Remember that our compression was good on solid areas of colour but a bit rubbish when it came down to dithered or complicated images. Consider:
HEADER: (26 bytes)-"BudgieScrn (compressed 2)" <cr> MODE: (1 byte)-Screen mode. PALETTE: (16 * 6 bytes)-Bytes giving R, G & B components of first flash colour. Bytes giving R, G, & B components of second flash colour. IMAGE DATA: If byte = 255, then loop data follows. Next byte is the colour. Third byte is a count (0 - 254) of how many pixels of this colour to fill in. If byte <> 255, then byte is regular screen memory. Just stick it straight to the screen...Immediately, two things should be noticed:
The program is commented, so shouldn't need any further explanation. Converting it to assembler is left as an exercise for the reader.
REM >Ssaver2 REM Compress sprites using BudgieSoft compression algorithm REM (type 2). REM REM by Richard Murray Winter 1997 REM REM Downloaded from: http://www.heyrick.co.uk/assembler/ : ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END : REM Open output file (put your own path here, or read from REM command line). x%=OPENOUT("IDEFS::Willow.$.WWW.WWWsite.assembler.sw.screen2/raw") : REM Write out the identification header... BPUT#x%, "BudgieScrn (compressed 2)" : REM ...and the current screenmode. BPUT#x%, MODE : REM Build up palette entries, and write out. FOR loop% = 0 TO 15 SYS "OS_ReadPalette", loop%, 16 TO ,,f%, s% r1% = (f% >> 8) AND 255 : REM First flash colour REM red component g1% = (f% >> 16) AND 255 : REM First flash colour REM green component b1% = (f% >> 24) AND 255 : REM First flash colour REM blue component r2% = (s% >> 8) AND 255 : REM Second flash colour REM red component g2% = (s% >> 16) AND 255 : REM Second flash colour REM green component b2% = (s% >> 24) AND 255 : REM Second flash colour REM blue component BPUT#x%, r1% : BPUT#x%, g1% : BPUT#x%, b1% BPUT#x%, r2% : BPUT#x%, g2% : BPUT#x%, b2% NEXT : REM Get our screen sizes. This is not written to file. The REM loader will have to figure out it's own screen dimensions. REM Then switch off the cursor. DIM b% 19 b%!0=149 : b%!4=7 : b%!8=-1 SYS "OS_ReadVduVariables", b%, b%+12 base%=b%!12 : length%=b%!16 :end%=base%+length% SYS "XOS_RemoveCursors" : : REM Now read from base% to end% (via posn%) calculating the REM data, writing it out to file as necessary. : posn% = base% REPEAT REM Read four pixels (two bytes) at a time. REM If they match, begin a loop. col1% = posn%?0 col2% = posn%?1 IF col1% = col2% THEN REM They match, so... BPUT#x%, 255 BPUT#x%, col1% counter% = 0 REPEAT col1% = posn%?0 IF col1% = col2% THEN counter%+=1 posn%+=1 ENDIF UNTIL counter% = 255 OR (col1% <> col2%) BPUT#x%, counter% ELSE REM Otherwise write out pixels. BPUT#x%, col1% IF col1% = 255 THEN REM A little hack for colour 15 double (byte = 255). BPUT#x%, col1% BPUT#x%, 0 ENDIF posn% += 1 ENDIF REM Until end. UNTIL posn% >=end% : REM Close file and exit. CLOSE#x% ENDApart from a few little changes in the loader routine, it is very similar to the existing loader. The differences, mainly, are:
.main_loop Read a byte. Is it '255'? If so, branch with link to read_loop. Write byte (pixel pair) to screen. Look to see if we are at the end of screen. If not, branch to main_loop. Close and exit etc etc. .read_loop Read colour byte. Read length byte. .loop2 Subtract 1 from length. Write colour byte to screen. Length is zero? If not, branch to loop2. End of screen? If not, branch to main_loop. Otherwise exit.I'll leave you to compare the above flow with the program itself. It is commented so you shouldn't have too many problems.
To current date there is no type 2 saver in assembler. I use the BASIC code mentioned previously. So instead I will treat you to a loader that handles all three versions.
The code is commented, but it is not split into annotated chunks as before. You should be able to follow what is going on.
REM >Sloader2 BudgieSoft screen loader (all versions) REM Version 0.21 REM REM by Richard Murray Winter 1997 REM REM Downloaded from: http://www.heyrick.co.uk/assembler/ : ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END : PROCassemble OSCLI("Save <Obey$Dir>.Loader "+STR$~(code%)+" "+STR$~(O%)) OSCLI("SetType <Obey$Dir>.Loader &FFC") END : DEFPROCassemble DIM code% &1000 FOR pass% = 4 TO 6 STEP 2 P%=0 O%=code% [ OPT pass% MOV R10, R14 MOV R0, #&4C SWI "XOS_Find" BVS exit MOV R5, R0 ; Skip header MOV R8, #0 .skip_header MOV R1, R5 BL read_byte ADD R8, R8, #1 CMP R8, #23 BLT skip_header MOV R1, R5 BL read_byte CMP R0, #10 ; Newline - version 0. BEQ version_zero CMP R0, #49 ; '1' - version 1. BEQ version_one CMP R0, #50 ; '2' - version 2. BEQ version_two ADR R0, unknown_string SWI "OS_PrettyPrint" SWI "OS_NewLine" SWI "OS_NewLine" B exit .unknown_string EQUS "Unable to load this screen - the format" EQUS " has not been recognised..." EQUB 0 ALIGN ; *** VERSION ZERO HANDLER *** .version_zero B load_old_format ; *** VERSION ONE HANDLER *** .version_one MOV R1, R5 BL read_byte BL read_byte B load_old_format ; *** VERSION TWO HANDLER .version_two MOV R1, R5 BL read_byte BL read_byte B load_new_format ; *** OLD FORMAT LOADER *** .load_old_format BL switch_screen_mode BL get_screen_info BL load_palette .old_loop MOV R1, R5 BL read_byte MOV R4, R0 BL read_byte .old_loop2 SUB R0, R0, #1 STRB R4, [R2, #1]! CMP R0, #0 BGT old_loop2 CMP R2, R3 BLT old_loop B exit ; *** NEW FORMAT LOADER *** .load_new_format BL switch_screen_mode BL get_screen_info BL load_palette .new_loop MOV R1, R5 BL read_byte CMP R0, #255 BLEQ read_loop STRB R0, [R2, #1]! CMP R2, R3 BLT new_loop B exit ; *** RESOURCES *** .switch_screen_mode MOV R12, R14 MOV R0, #&87 SWI "XOS_Byte" MOV R1, R5 BL read_byte CMP R0, R2 SWINE &100 + 22 SWINE "OS_WriteC" MOV PC, R12 .get_screen_info 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 MOV PC, R14 .load_palette MOV R12, R14 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 MOV R0, #0 MOV PC, R12 .exit MOV R2, R0 MOV R1, R5 MOV R0, #0 SWI "XOS_Find" MOV PC, R10 .read_byte SWI "XOS_BGet" MOVVC PC, R14 .read_loop ; Read and deal with loop data. MOV R1, R5 BL read_byte ; Read colour MOV R4, R0 BL read_byte ; Read length .loop2 SUB R0, R0, #1 STRB R4, [R2, #1]! CMP R0, #0 ; More to loop? BGT loop2 CMP R2, R3 ; End of screen? BLT new_loop B exit .vdu_block EQUD 149 EQUD 7 EQUD -1 EQUD 0 EQUD 0 .os_block EQUD 0 EQUB 0 ALIGN ] NEXT ENDPROCThis concludes our series on creating compressed screen software. If you wish to write an assembler saver for type 2 files, please do so. Likewise, bug reports and such would also be welcomed.
By way of comparison, here is a type 1 version of the above image. It is a moderately balanced image, a textured JPEG and textured windows alongside plain windows.
The type 1 screen is 159771 bytes.
The type 2 screen is 123369 bytes.