UnSqueeze 16.4 KB
Newer Older
Neil Turton's avatar
Neil Turton committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > UnSqueeze

; Unsqueezes a file that has had squeeze applied to it. Works on both
; AIF and non AIF images. Can only unsqueeze if recognises unsqueezer
; code (this new vrsion has to be much more careful, and use
; OS_SynchroniseCodeAreas)

; --------------------------------------------------------------------------

        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:Tokens

        GET     Hdr:Proc
        GET     Hdr:FSNumbers
        GET     Hdr:HighFSI
        GET     Hdr:FileTypes
        GET     Hdr:ModHand
        GET     Hdr:Wimp

; --------------------------------------------------------------------------

38 39
        AREA    |UnSqueeze$$Code|, CODE, READONLY, PIC

Neil Turton's avatar
Neil Turton committed
40 41 42 43 44 45 46 47 48
Module_BaseAddr
        DCD     0                       ; No start entry
        DCD     0                       ; No init entry
        DCD     0                       ; No die entry
        DCD     0                       ; No service entry
        DCD     UnSqueeze_Title    - Module_BaseAddr
        DCD     UnSqueeze_HelpStr  - Module_BaseAddr
        DCD     UnSqueeze_HC_Table - Module_BaseAddr
        DCD     0                       ; No SWI Chunk
49 50 51 52 53 54 55 56 57
    [ :LNOT: No32bitCode
        DCD     0                       ; SWI handler
        DCD     0                       ; SWI decode table
        DCD     0                       ; SWI decode code
        DCD     0                       ; Messages file
        DCD     UnSqueeze_ModFlags - Module_BaseAddr
UnSqueeze_ModFlags
        DCD     ModuleFlag_32bit
    ]
Neil Turton's avatar
Neil Turton committed
58 59 60 61 62 63 64 65 66

UnSqueeze_HC_Table
        Command UnSqueeze, 2, 1         ; Name, Max, Min
        DCB     0

UnSqueeze_Title
        DCB     "UnSqueeze", 0

UnSqueeze_HelpStr
67
        DCB     "UnSqueeze", 9, "1.26 (15 Jan 2004)", 0
Neil Turton's avatar
Neil Turton committed
68

Ben Avison's avatar
Ben Avison committed
69
        GET     TokHelpSrc.s
Neil Turton's avatar
Neil Turton committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

        ALIGN

; --------------------------------------------------------------------------

; Workspace relative to r11, which contains &8000 ie. our workspace is
; in application space.

; It is used to hold various things across decompression, since we have to
; assume that the decompression code trashes ALL registers.

; The first two items (ldr_pc_return_address and return_address) must
; be at &8000 and &8004 or else.

                        ^       0, r11
ldr_pc_return_address   #       4       ; LDR pc, [pc, #-4]
return_address          #       4       ; Address of after_decomp
user_stack_ptr          #       4       ; Pointer to user stack

flags_word              #       4       ;
output_file             #       4       ; Pointer to filename.
output_length           #       4       ; Length of decompressed image.
end_data                #       0

; This is where we load the image to be unsqueezed ie. on the end.
; Note that this is not always an AIF image.

image_start             #       0
bl_decompress           #       4
bl_selfreloc            #       4
bl_zeroinit             #       4
bl_imageentry           #       4
aif_swi_exit            #       4

; --------------------------------------------------------------------------

; The command tail is in r0, so keep it safe.

108
UnSqueeze_Code Entry "r7-r11"
Neil Turton's avatar
Neil Turton committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
        MOV     r11, #&8000                     ; Workspace pointer
        MOV     r10, r0

; Get current memory limit and put it in r9.

        SWI     XOS_GetEnv
        EXIT    VS
        MOV     r9, r1

; Check that we have enough memory for our workspace usage against
; the current memory limit.

        ADR     r5, end_data
        CMP     r5, r9                         ; Check if will already fit
        BLO     workspace_valid

; Allocate slot size we need (in r0) and check it robustly (by using
; OS_GetEnv) rather than trusting returned slot size, in case we are
; outside the wimp altogether.

        MOV     r0, #(:INDEX: end_data)
        MOV     r1, #-1
        SWI     XWimp_SlotSize
        SWIVC   XOS_GetEnv
        EXIT    VS

        CMP     r5, r1
        ADRHS   r0, error_block_no_memory
        BHS     error_exit
        MOV     r9, r1                          ; Remember new memory limit

workspace_valid

; Need to check whether there are one or two filenames present.
; Get the first name back from r10, and also put it in the output name
; in case we don't find a second name.

        MOV     r0, r10
        STR     r0, output_file
name_loop
        LDRB    r1, [r0], #1
        CMP     r1, #" "
        BHI     name_loop
        BLO     end_names

; We have found the space(s) preceding the second name.

space_loop
        LDRB    r1, [r0], #1
        CMP     r1, #" "
        BEQ     space_loop

; We have found the second name. r0 is in fact one greater than we want.

        SUBHI   r0, r0, #1
        STRHI   r0, output_file

end_names

; Read the length of the input file, and verify file type.

        MOV     r0, #OSFile_ReadInfo            ; Uses File$Path
        MOV     r1, r10                         ; Input file
        SWI     XOS_File
        EXIT    VS

; Check that it is a file.

        TEQ     r0, #object_file
        BNE     input_file_error                ; Not found/is a directory

; Check that it has a filetype of Application (FF8)

        LDR     r7, =FileType_Application
        MOV     r6, r2, LSL #12                 ; r2 is load address
        MOV     r6, r6, LSR #20                 ; Get filetype into r6
        TEQ     r6, r7
        ADRNE   r0, error_block_not_app
        BNE     error_exit

; Check that we have enough memory to load it - length is in r4.
; Get size of slot we need in r0, get top of memory we need (ie.
; r0 + &8000) in r5.

        ADD     r0, r4, #(:INDEX: image_start)
        ADD     r5, r0, #&8000
        CMP     r5, r9                         ; Check if will already fit
        BLO     load_slot_ok

; Allocate slot size we need (in R0) and check it robustly (by using
; OS_GetEnv) rather than trusting returned slot size, in case we are
; outside the wimp altogether.

        MOV     r1, #-1
        SWI     XWimp_SlotSize
        SWIVC   XOS_GetEnv
        EXIT    VS

        CMP     r5, r1
        ADRHS   r0, error_block_no_memory
        BHS     error_exit
        MOV     r9, r1                          ; Remember new memory limit

load_slot_ok

; Load the image into memory.

        MOV     r0, #OSFile_Load                ; Use File$Path
        MOV     r1, r10                         ; Input file
        ADR     r2, image_start                 ; Load it here
        MOV     r3, #0                          ; Pay attention to R2
        SWI     XOS_File
        EXIT    VS

; Check if it is an AIF image. Note that r4 is still the length of the
; image from the OS_File, and this is used in not_aif_image.

        LDR     r0, aif_swi_exit
        LDR     r1, my_swi_exit
        TEQ     r0, r1
        BNE     not_aif_image

; Check if it is squeezed.

        LDR     r0, bl_decompress               ; Instruction
        TEQ     r0, #&FB000000                  ; Code if not squeezed
        BEQ     error_not_squeezed

; Check if it is a BL instruction.

        AND     r0, r0, #&FF000000
        TEQ     r0, #&EB000000
        ADRNE   r0, error_block_weird_image
        BNE     error_exit

        BL      patch_unsqueeze
        CMP     r0,#0
        BNE     error_exit

        MOV     r0,#1                    ;flag as AIF image, not intercepted yet
        STR     r0,flags_word

; Remember that size is 6 words before the code, and remember that the
; 32 words of AIF header needs adding in to the size, and go to the
; size evaluation code.

        MOV     r10, #-(6 * 4)                  ; Offset backwards
        MOV     r8, #(32 * 4)                   ; Amount to add
        B       evaluate_size

error_not_squeezed
        ADR     r0, error_block_not_squeezed
        B       error_exit

; For a non AIF image, the first word should be a branch instruction.

not_aif_image
        LDR     r0, image_start
        AND     r0, r0, #&FF000000
        TEQ     r0, #&EA000000
        BNE     error_not_squeezed

        BL      patch_unsqueeze
        CMP     r0,#0
        BNE     error_exit

; Remember that size is 9 words before the code, and remember that nothing
; needs adding to the size, and go to the size evaluation code.

        MOV     r10, #-(9 * 4)                  ; Offset backwards
        MOV     r8, #0                          ; Amount to add

        MOV     r0,#0                           ;flag nonAIF and not intercepted
        STR     r0,flags_word

; ** Drop through **

; Work out how big it will be when unsqueezed. This is the size in r8
; (AIF header of 32 words or zero) plus the contents of the location
; r10 (six or nine) words before the start of the decompression code.
; This sometimes results in four extra bytes of zeros on the end of
; the file.

evaluate_size
        LDR     r0, bl_decompress
294
        BIC     r0, r0, #&FF000000              ; Get offset (assume positive)
Neil Turton's avatar
Neil Turton committed
295
        ADR     r1, bl_zeroinit                 ; Add 8 for pipeline in BL
296
        ADD     r0, r1, r0, LSL #2              ; Add address where loaded
Neil Turton's avatar
Neil Turton committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
        LDR     r0, [r0, r10]                   ; Load XX words previous
        ADD     r0, r0, r8                      ; Add YY words on
        STR     r0, output_length               ; Save output file length

; Now make sure we have got enough memory for the job. Need to add 24K
; for un-squeezing workspace.

        ADD     r0, r0, #(:INDEX: image_start)
        ADD     r0, r0, #(24 * 1024)
        ADD     r5, r0, #&8000
        CMP     r5, r9                         ; Check if will already fit
        BLO     decomp_slot_ok

; Allocate slot size we need (in R0) and check it robustly (by using
; OS_GetEnv) rather than trusting returned slot size, in case we are
; outside the wimp altogether.

        MOV     r1, #-1
        SWI     XWimp_SlotSize
        SWIVC   XOS_GetEnv
        EXIT    VS

        CMP     r5, r1
        ADRHS   r0, error_block_no_memory
        BHS     error_exit

decomp_slot_ok

; Drop into user mode - the unsqueezing code assumes that it is in user
; mode and anyway it is better to be in user mode (memory protection).
; Put a NOP after the mode change, as the following code access R11
; which is a banked register.

330 331 332
      [ No32bitCode
        TEQ     pc, #0
      |
333 334 335
        MRS     r0, CPSR
        BIC     r0, r0, #&F
        MSR     CPSR_c, r0
336
      ]
Neil Turton's avatar
Neil Turton committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

; Put the return instruction and return address into our workspace, along
; with the user mode stack pointer which must also be preserved, and call
; the relocatable decompression code.

        LDR     r0, ldr_pc_pc_minus_4
        STR     r0, ldr_pc_return_address
        ADR     r1, after_decomp
        STR     r1, return_address
        STR     sp, user_stack_ptr

        MOV     r0,#0
        SWI     XOS_SynchroniseCodeAreas

        ADR     pc, bl_decompress

; --------------------------------------------------------------------------

; Files must be of type Application to be unsqueezable.

error_block_not_app
        DCD     0
        DCB     "UnSqueeze: "
        DCB     "Input file must be of type Application", 0
        ALIGN

; --------------------------------------------------------------------------

; Couldn't get enough application space.

error_block_no_memory
        DCD     0
        DCB     "UnSqueeze: "
        DCB     "Failed to allocate sufficient Application memory", 0
        ALIGN

; --------------------------------------------------------------------------

; RunImage not squeezed.

error_block_not_squeezed
        DCD     0
        DCB     "UnSqueeze: "
        DCB     "Input file is not squeezed", 0
        ALIGN

; --------------------------------------------------------------------------

; Not a RunImage at all.

error_block_weird_image
        DCD     0
        DCB     "UnSqueeze: "
        DCB     "Input file is not a valid AIF run image", 0
        ALIGN

; --------------------------------------------------------------------------

error_block_unrecogsqf
        DCD     0
        DCB     "UnSqueeze: "
        DCB     "Unrecognised squeeze format",0
        ALIGN

; --------------------------------------------------------------------------

; Serious error situation - input file problem. Use the FileSwitch
; facility to generate a "File 'foo' not found" type error.

; Input - r0 = Object type (None/File/Dir/Image)
;         r1 = File name

input_file_error
        MOV     r2, r0
        MOV     r0, #OSFile_MakeError
        SWI     XOS_File
error_exit                                      ; Jumped to with r0 -> error
        SETV
        EXIT

; --------------------------------------------------------------------------

;entry: r4 = image length
;exit:  r0 = 0 for success, error ptr for failure

patch_unsqueeze ROUT
        STMFD   r13!,{r1-r7,lr}

        ADR     r0,image_start
        ADD     r1,r0,r4
        BIC     r1,r1,#3           ;round down to word
        SUB     r1,r1,#UnsqSigSize

        LDR     r2,[r0]            ;start looking where the branch goes
        BIC     r2,r2,#&FF000000
        ADD     r0,r0,r2,LSL #2    ;add offset of branch (should be +ve)
        ADD     r0,r0,#8           ;account for PC+8

        ADR     r3,UnsqSignature
        LDR     r5,[r3],#4

01
        LDR     r2,[r0],#4
        CMP     r2,r5
        BEQ     %FT03
        CMP     r0,r1
        BLO     %BT01
;give up
02
        ADR     r0,error_block_unrecogsqf
        LDMFD   r13!,{r1-r7,pc}

;matched 1st word of sig
03
        MOV     r6,#UnsqSigSize-4
04
        LDR     r2,[r0],#4
        LDR     r7,[r3],#4
        CMP     r2,r7
        BNE     %BT02
        SUBS    r6,r6,#4
        BNE     %BT04
459
;matched whole sig, patch next word
Neil Turton's avatar
Neil Turton committed
460
        LDR     r7,mov_pc_8000
461
        STR     r7,[r0,#0]
Neil Turton's avatar
Neil Turton committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
;now find the MOV PC,R8 and patch it
        LDR     r5,mov_pc_r8
        ADD     r1,r1,#UnsqSigSize-4
05
        LDR     r2,[r0],#4
        CMP     r2,r5
        BEQ     %FT06
        CMP     r0,r1
        BLO     %BT05
        B       %BT02
06
        LDR     r7,mov_pc_8000
        STR     r7,[r0,#-4]

        MOV     r0,#0
        LDMFD   r13!,{r1-r7,pc}

;code to look for
UnsqSignature
        LDMIA   R5!,{R0-R3}
        STMIA   R7!,{R0-R3}
        CMP     R5,R6
        BLT     UnsqSignature
485
UnsqSigSize * 4*4
Neil Turton's avatar
Neil Turton committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

; --------------------------------------------------------------------------


after_decomp
        MOV     r1,  r11
        MOV     r11, #&8000                     ; Workspace

        LDR     r0,flags_word
        TST     r0,#2
        BNE     %FT01

;this is the first interception, synchronise and mov pc,r4
        ORR     r0,r0,#2       ;we've intercepted once
        STR     r0,flags_word
        MOV     r0,#0
        SWI     XOS_SynchroniseCodeAreas
        MOV     r11,r1
        MOV     PC,r4

; This is where we return to after the decompression code has run.
; Restore the user mode stack pointer, get back into SVC mode (which gets
; us the SVC stack pointer back), and restore our workspace pointer.
; Use non X form - if X form failed we couldn't return anyway.
01
        LDR     sp, user_stack_ptr

; We don't use the X form of the SWI. If it returned an error we couldn't
; do anything with it, as our return address is on the SVC stack.

        SWI     OS_EnterOS

; Save the resulting file out.

        MOV     r0, #OSFile_SaveStamp
        LDR     r1, output_file
        LDR     r2, =FileType_Application
        ADR     r4, image_start
        LDR     r10, output_length
        ADD     r5, r4, r10
        SWI     XOS_File

; Terminate, either with or without error.

        EXIT

; --------------------------------------------------------------------------

; Some static data.

rcc_string
        DCB     "rcc "                  ; Searched for if not AIF image
mov_pc_r8
        MOV     pc, r8                  ; Before "rcc " - used if not AIF
mov_pc_8000
        MOV     pc, #&8000              ; Used to get control back to us
ldr_pc_pc_minus_4
        LDR     pc, [pc, #-4]           ; Gets us back into module code
my_swi_exit
        SWI     OS_Exit                 ; Checked against in AIF image

; --------------------------------------------------------------------------

        END