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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 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 108 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 294 295 296 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 330 331 332 333 334 335 336 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 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 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
|
;*****************************************************************************/
;* */
;* modload.s */
;* */
;* o65 module loader for the cc65 library */
;* */
;* */
;* */
;* (C) 2002 Ullrich von Bassewitz */
;* Wacholderweg 14 */
;* D-70597 Stuttgart */
;* EMail: uz@musoftware.de */
;* */
;* */
;* This software is provided 'as-is', without any expressed or implied */
;* warranty. In no event will the authors be held liable for any damages */
;* arising from the use of this software. */
;* */
;* Permission is granted to anyone to use this software for any purpose, */
;* including commercial applications, and to alter it and redistribute it */
;* freely, subject to the following restrictions: */
;* */
;* 1. The origin of this software must not be misrepresented; you must not */
;* claim that you wrote the original software. If you use this software */
;* in a product, an acknowledgment in the product documentation would be */
;* appreciated but is not required. */
;* 2. Altered source versions must be plainly marked as such, and must not */
;* be misrepresented as being the original software. */
;* 3. This notice may not be removed or altered from any source */
;* distribution. */
;* */
;*****************************************************************************/
.include "o65.inc"
.include "modload.inc"
.include "zeropage.inc"
.import pushax, pusha0, push0, push1, decax1
.import _malloc, _free, _bzero
.import __ZP_START__ ; Linker generated
.macpack generic
;------------------------------------------------------------------------------
; Variables stored in the register bank in the zero page. Placing the variables
; here will protect them when calling other C functions.
Module = regbank+0 ; Pointer to module memory
Ctrl = regbank+2 ; Pointer to mod_ctrl structure
TPtr = regbank+4 ; Pointer to module data for relocation
;------------------------------------------------------------------------------
; Static module data
.bss
; Save areas and error recovery data
Stack: .byte 0 ; Old stackpointer
RegBankSave: .res regbanksize ; Save area for register bank
; The header of the o65 file. Since we don't need the first 8 bytes any
; longer, once we've checked them, we will overlay them with other data to
; save a few bytes.
Header: .tag O65_HDR ; The o65 header
; Input
InputByte = Header ; Byte read from input
; Relocation
RelocVal = Header + 1 ; Relocation value
.data
Read: jmp $FFFF ; Jump to read routine
.rodata
ExpectedHdr:
.byte O65_MARKER_0, O65_MARKER_1 ; non C64 marker
.byte O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2 ; Magic ("o65")
.byte O65_VERSION ; Version
.word O65_MODE_CC65 ; Mode word
ExpectedHdrSize = * - ExpectedHdr
;------------------------------------------------------------------------------
; PushCallerData: Push the callerdata member from control structure onto the
; C stack.
.code
PushCallerData:
ldy #MOD_CTRL::CALLERDATA+1
lda (Ctrl),y
tax
dey
lda (Ctrl),y
jmp pushax
;------------------------------------------------------------------------------
; RestoreRegBank: Restore the register bank contents from the save area. Will
; destroy A and X (the latter will be zero on return).
.code
RestoreRegBank:
ldx #6
@L1: lda RegBankSave-1,x
sta regbank-1,x
dex
bne @L1
rts
;------------------------------------------------------------------------------
; GetReloc: Return a relocation value based on the segment in A.
; The routine uses some knowledge about the values to make the code shorter.
.code
GetReloc:
cmp #O65_SEGID_TEXT
bcc FormatError
cmp #O65_SEGID_ZP
beq @L1
bcs FormatError
; Text, data and bss segment
lda Module
ldx Module+1 ; Return start address of buffer
rts
; Zero page relocation
@L1: lda #<__ZP_START__
ldx #>__ZP_START__
rts
;------------------------------------------------------------------------------
; ReadByte: Read one byte with error checking into InputByte and A.
; ReadAndCheckError: Call read with the current C stack and check for errors.
.bss
ReadSize: .res 2
.code
ReadByte:
; C->read (C->callerdata, &B, 1)
jsr PushCallerData
lda #<InputByte
ldx #>InputByte
jsr pushax
ldx #0
lda #1
; This is a second entry point used by the other calls to Read
ReadAndCheckError:
sta ReadSize
stx ReadSize+1
jsr Read
; Check the return code and bail out in case of problems
cmp ReadSize
bne @L1
cpx ReadSize+1
beq @L2 ; Jump if ok
@L1: lda #MLOAD_ERR_READ
bne CleanupAndExit
; Done
@L2: lda InputByte ; If called ReadByte, load the byte read
Done: rts
;------------------------------------------------------------------------------
; FormatError: Bail out with an o65 format error
.code
FormatError:
lda #MLOAD_ERR_FMT
; bne CleanupAndExit ; Branch always
;------------------------------------------------------------------------------
; CleanupAndExit: Free any allocated resources, restore the stack and return
; to the caller.
.code
CleanupAndExit:
; Restore the stack so we may return to the caller from here
ldx Stack
txs
; Save the error return code
pha
; Check if we have to free the allocated block
lda Module
ldx Module+1
bne @L1
tay ; Test high byte
beq @L2
@L1: jsr _free ; Free the allocated block
; Restore the register bank
@L2: jsr RestoreRegBank
; Restore the error code and return to the caller
ldx #$00 ; Load the high byte
pla
rts
;------------------------------------------------------------------------------
; RelocSeg: Relocate the segment pointed to by a/x
.code
RelocSeg:
jsr decax1 ; Start value is segment-1
sta TPtr
stx TPtr+1
Loop: jsr ReadByte ; Read byte from relocation table
beq Done ; Bail out if end of table reached
cmp #255 ; Special offset?
bne @L1
; Increment offset by 254 and continue
lda TPtr
add #254
sta TPtr
bcc Loop
inc TPtr+1
jmp Loop
; Increment offset by A
@L1: add TPtr
sta TPtr
bcc @L2
inc TPtr+1
; Read the relocation byte, extract the segment id, fetch the corresponding
; relocation value and place it into ptr1
@L2: jsr ReadByte
and #O65_SEGID_MASK
jsr GetReloc
sta RelocVal
stx RelocVal+1
; Get the relocation byte again, this time extract the relocation type.
lda InputByte
and #O65_RTYPE_MASK
; Check for and handle the different relocation types.
cmp #O65_RTYPE_WORD
beq RelocWord
cmp #O65_RTYPE_HIGH
beq RelocHigh
cmp #O65_RTYPE_LOW
bne FormatError
; Relocate the low byte
RelocLow:
ldy #0
clc
lda RelocVal
bcc AddCommon
; Relocate a high byte
RelocHigh:
jsr ReadByte ; Read low byte from relocation table
ldy #0
clc
adc RelocVal ; We just need the carry
AddHigh:
lda RelocVal+1
AddCommon:
adc (TPtr),y
sta (TPtr),y
jmp Loop ; Done, next entry
; Relocate a word
RelocWord:
ldy #0
clc
lda RelocVal
adc (TPtr),y
sta (TPtr),y
iny
bne AddHigh ; Branch always (add high byte)
;------------------------------------------------------------------------------
; mod_load: Load and relocate an o65 module
.code
_mod_load:
; Save the register bank and clear the Module pointer
pha
ldy #6
@L1: lda regbank-1,y
sta RegBankSave-1,y
dey
bne @L1
sty Module
sty Module+1
pla
; Save the passed parameter
sta Ctrl
stx Ctrl+1
; Save the stack pointer so we can bail out even from subroutines
tsx
stx Stack
; Get the read function pointer from the control structure and place it into
; our call vector
ldy #MOD_CTRL::READ
lda (Ctrl),y
sta Read+1
iny
lda (Ctrl),y
sta Read+2
; Read the o65 header: C->read (C->callerdata, &H, sizeof (H))
jsr PushCallerData
lda #<Header
ldx #>Header
jsr pushax
lda #.sizeof(O65_HDR)
ldx #0 ; Always less than 256
jsr ReadAndCheckError ; Bails out in case of errors
; We read the o65 header successfully. Validate it.
ldy #ExpectedHdrSize-1
ValidateHeader:
lda Header,y
cmp ExpectedHdr,y
bne HeaderError
dey
bpl ValidateHeader
; Header is ok as far as we can say now. Read all options, check for the
; OS option and ignore all others. The OS option contains a version number
; and the module id as additional data.
iny ; Y = $00
sty TPtr+1 ; Flag for OS option read
Opt: jsr ReadByte ; Read the length byte
beq OptDone ; Jump if done
sta TPtr ; Use TPtr as a counter
; An option has a length of at least 2 bytes
cmp #2
bcc HeaderError ; Must be 2 bytes total at least
; Check for the OS option
dec TPtr
jsr ReadByte ; Get the option type
cmp #O65_OPT_OS ; OS option?
bne SkipOpt ; No: Skip
lda TPtr ; Get remaining length+1
cmp #5 ; CC65 has 6 bytes total
bne OSError
jsr ReadByte ; Get the operating system
cmp #O65_OS_CC65
bne OSError ; Wrong operating system
jsr ReadByte ; Get the version number, expect zero
bne OSError ; Wrong version
jsr ReadByte ; Get low byte of id
ldy #MOD_CTRL::MODULE_ID
sta (Ctrl),y
jsr ReadByte
ldy #MOD_CTRL::MODULE_ID+1
sta (Ctrl),y
inc TPtr+1 ; Remember that we got the OS
jmp Opt
; Skip one option
SkipOpt:
dec TPtr
beq Opt ; Next option
jsr ReadByte ; Skip one byte
jmp SkipOpt
; Operating system error
OSError:
lda #MLOAD_ERR_OS
jmp CleanupAndExit
; Options done, check that we got the OS option
OptDone:
lda TPtr+1
bne CalcSizes
; Entry point for header errors
HeaderError:
lda #MLOAD_ERR_HDR
jmp CleanupAndExit
; Skipped all options. Calculate the size of text+data and of text+data+bss
; (the latter is the size of the memory block we need). We will store the
; total module size also into the control structure for evaluation by the
; caller
CalcSizes:
lda Header + O65_HDR::TLEN
add Header + O65_HDR::DLEN
sta TPtr
lda Header + O65_HDR::TLEN + 1
adc Header + O65_HDR::DLEN + 1
sta TPtr+1
lda TPtr
add Header + O65_HDR::BLEN
pha ; Save low byte of total size
ldy #MOD_CTRL::MODULE_SIZE
sta (Ctrl),y
lda TPtr+1
adc Header + O65_HDR::BLEN + 1
iny
sta (Ctrl),y
tax
pla ; Restore low byte of total size
; Total memory size is now in a/x. Allocate memory and remember the result,
; both, locally and in the control structure so it the caller can access
; the memory block. After that, check if we got the requested memory.
jsr _malloc
sta Module
stx Module+1
ldy #MOD_CTRL::MODULE
sta (Ctrl),y
txa
iny
sta (Ctrl),y
ora Module
bne GotMem
; Could not allocate memory
lda #MLOAD_ERR_MEM
jmp CleanupAndExit
; Control structure is complete now. Clear the bss segment.
; bzero (bss_addr, bss_size)
GotMem: lda Module
add TPtr
pha
lda Module+1
adc TPtr+1 ; Module + tlen + dlen
tax
pla
jsr pushax
lda Header + O65_HDR::BLEN
ldx Header + O65_HDR::BLEN+1
jsr _bzero ; bzero (bss, bss_size);
; Load code and data segment into memory. The sum of the sizes of
; code+data segment is still in TPtr.
; C->read (C->callerdata, C->module, H.tlen + H.dlen)
jsr PushCallerData
lda Module
ldx Module+1
jsr pushax
lda TPtr
ldx TPtr+1
jsr ReadAndCheckError ; Bails out in case of errors
; We've got the code and data segments in memory. Next section contains
; undefined references which we don't support. So check if the count of
; undefined references is actually zero.
jsr ReadByte
bne Undef
jsr ReadByte
beq Reloc
Undef: jmp FormatError
; Number of undefined references was zero. Next come the relocation tables
; for code and data segment. Relocate the code segment
Reloc: lda Module
ldx Module + 1 ; Code segment address
jsr RelocSeg
; Relocate the data segment
lda Module
add Header + O65_HDR::TLEN
pha
lda Module + 1
adc Header + O65_HDR::TLEN + 1
tax
pla ; Data segment address in a/x
jsr RelocSeg
; We're done. Restore the register bank and return a success code
jsr RestoreRegBank ; X will be zero on return
lda #MLOAD_OK
rts
|