1 ;*****************************************************************************/
5 ;* o65 module loader for the cc65 library */
9 ;* (C) 2002 Ullrich von Bassewitz */
11 ;* D-70597 Stuttgart */
12 ;* EMail: uz@musoftware.de */
15 ;* This software is provided 'as-is', without any expressed or implied */
16 ;* warranty. In no event will the authors be held liable for any damages */
17 ;* arising from the use of this software. */
19 ;* Permission is granted to anyone to use this software for any purpose, */
20 ;* including commercial applications, and to alter it and redistribute it */
21 ;* freely, subject to the following restrictions: */
23 ;* 1. The origin of this software must not be misrepresented; you must not */
24 ;* claim that you wrote the original software. If you use this software */
25 ;* in a product, an acknowledgment in the product documentation would be */
26 ;* appreciated but is not required. */
27 ;* 2. Altered source versions must be plainly marked as such, and must not */
28 ;* be misrepresented as being the original software. */
29 ;* 3. This notice may not be removed or altered from any source */
32 ;*****************************************************************************/
37 .include "modload.inc"
38 .include "zeropage.inc"
40 .import pushax, pusha0, push0, push1, decax1
41 .import _malloc, _free, _bzero
42 .import __ZP_START__ ; Linker generated
46 ;------------------------------------------------------------------------------
47 ; Variables stored in the register bank in the zero page. Placing the variables
48 ; here will protect them when calling other C functions.
50 Module = regbank+0 ; Pointer to module memory
51 Ctrl = regbank+2 ; Pointer to mod_ctrl structure
52 TPtr = regbank+4 ; Pointer to module data for relocation
54 ;------------------------------------------------------------------------------
59 ; Save areas and error recovery data
60 Stack: .byte 0 ; Old stackpointer
61 RegBankSave: .res regbanksize ; Save area for register bank
63 ; The header of the o65 file. Since we don't need the first 8 bytes any
64 ; longer, once we've checked them, we will overlay them with other data to
66 Header: .tag O65_HDR ; The o65 header
69 InputByte = Header ; Byte read from input
72 RelocVal = Header + 1 ; Relocation value
75 Read: jmp $FFFF ; Jump to read routine
79 .byte O65_MARKER_0, O65_MARKER_1 ; non C64 marker
80 .byte O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2 ; Magic ("o65")
81 .byte O65_VERSION ; Version
82 .word O65_MODE_CC65 ; Mode word
84 ExpectedHdrSize = * - ExpectedHdr
87 ;------------------------------------------------------------------------------
88 ; PushCallerData: Push the callerdata member from control structure onto the
93 ldy #MOD_CTRL::CALLERDATA+1
100 ;------------------------------------------------------------------------------
101 ; RestoreRegBank: Restore the register bank contents from the save area. Will
102 ; destroy A and X (the latter will be zero on return).
107 @L1: lda RegBankSave-1,x
113 ;------------------------------------------------------------------------------
114 ; GetReloc: Return a relocation value based on the segment in A.
115 ; The routine uses some knowledge about the values to make the code shorter.
125 ; Text, data and bss segment
128 ldx Module+1 ; Return start address of buffer
131 ; Zero page relocation
133 @L1: lda #<__ZP_START__
137 ;------------------------------------------------------------------------------
138 ; ReadByte: Read one byte with error checking into InputByte and A.
139 ; ReadAndCheckError: Call read with the current C stack and check for errors.
147 ; C->read (C->callerdata, &B, 1)
156 ; This is a second entry point used by the other calls to Read
163 ; Check the return code and bail out in case of problems
169 @L1: lda #MLOAD_ERR_READ
174 @L2: lda InputByte ; If called ReadByte, load the byte read
177 ;------------------------------------------------------------------------------
178 ; FormatError: Bail out with an o65 format error
183 ; bne CleanupAndExit ; Branch always
185 ;------------------------------------------------------------------------------
186 ; CleanupAndExit: Free any allocated resources, restore the stack and return
192 ; Restore the stack so we may return to the caller from here
197 ; Save the error return code
201 ; Check if we have to free the allocated block
208 @L1: jsr _free ; Free the allocated block
210 ; Restore the register bank
212 @L2: jsr RestoreRegBank
214 ; Restore the error code and return to the caller
216 ldx #$00 ; Load the high byte
220 ;------------------------------------------------------------------------------
221 ; RelocSeg: Relocate the segment pointed to by a/x
225 jsr decax1 ; Start value is segment-1
229 Loop: jsr ReadByte ; Read byte from relocation table
230 beq Done ; Bail out if end of table reached
232 cmp #255 ; Special offset?
235 ; Increment offset by 254 and continue
244 ; Increment offset by A
251 ; Read the relocation byte, extract the segment id, fetch the corresponding
252 ; relocation value and place it into ptr1
260 ; Get the relocation byte again, this time extract the relocation type.
265 ; Check for and handle the different relocation types.
274 ; Relocate the low byte
282 ; Relocate a high byte
285 jsr ReadByte ; Read low byte from relocation table
288 adc RelocVal ; We just need the carry
294 jmp Loop ; Done, next entry
305 bne AddHigh ; Branch always (add high byte)
307 ;------------------------------------------------------------------------------
308 ; mod_load: Load and relocate an o65 module
313 ; Save the register bank and clear the Module pointer
325 ; Save the passed parameter
330 ; Save the stack pointer so we can bail out even from subroutines
335 ; Get the read function pointer from the control structure and place it into
345 ; Read the o65 header: C->read (C->callerdata, &H, sizeof (H))
351 lda #.sizeof(O65_HDR)
352 ldx #0 ; Always less than 256
353 jsr ReadAndCheckError ; Bails out in case of errors
355 ; We read the o65 header successfully. Validate it.
357 ldy #ExpectedHdrSize-1
365 ; Header is ok as far as we can say now. Read all options, check for the
366 ; OS option and ignore all others. The OS option contains a version number
367 ; and the module id as additional data.
370 sty TPtr+1 ; Flag for OS option read
371 Opt: jsr ReadByte ; Read the length byte
372 beq OptDone ; Jump if done
373 sta TPtr ; Use TPtr as a counter
375 ; An option has a length of at least 2 bytes
378 bcc HeaderError ; Must be 2 bytes total at least
380 ; Check for the OS option
383 jsr ReadByte ; Get the option type
384 cmp #O65_OPT_OS ; OS option?
385 bne SkipOpt ; No: Skip
387 lda TPtr ; Get remaining length+1
388 cmp #5 ; CC65 has 6 bytes total
391 jsr ReadByte ; Get the operating system
393 bne OSError ; Wrong operating system
395 jsr ReadByte ; Get the version number, expect zero
396 bne OSError ; Wrong version
398 jsr ReadByte ; Get low byte of id
399 ldy #MOD_CTRL::MODULE_ID
402 ldy #MOD_CTRL::MODULE_ID+1
405 inc TPtr+1 ; Remember that we got the OS
413 beq Opt ; Next option
414 jsr ReadByte ; Skip one byte
417 ; Operating system error
423 ; Options done, check that we got the OS option
429 ; Entry point for header errors
435 ; Skipped all options. Calculate the size of text+data and of text+data+bss
436 ; (the latter is the size of the memory block we need). We will store the
437 ; total module size also into the control structure for evaluation by the
441 lda Header + O65_HDR::TLEN
442 add Header + O65_HDR::DLEN
444 lda Header + O65_HDR::TLEN + 1
445 adc Header + O65_HDR::DLEN + 1
448 add Header + O65_HDR::BLEN
449 pha ; Save low byte of total size
450 ldy #MOD_CTRL::MODULE_SIZE
453 adc Header + O65_HDR::BLEN + 1
457 pla ; Restore low byte of total size
459 ; Total memory size is now in a/x. Allocate memory and remember the result,
460 ; both, locally and in the control structure so it the caller can access
461 ; the memory block. After that, check if we got the requested memory.
467 ldy #MOD_CTRL::MODULE
475 ; Could not allocate memory
480 ; Control structure is complete now. Clear the bss segment.
481 ; bzero (bss_addr, bss_size)
487 adc TPtr+1 ; Module + tlen + dlen
491 lda Header + O65_HDR::BLEN
492 ldx Header + O65_HDR::BLEN+1
493 jsr _bzero ; bzero (bss, bss_size);
495 ; Load code and data segment into memory. The sum of the sizes of
496 ; code+data segment is still in TPtr.
497 ; C->read (C->callerdata, C->module, H.tlen + H.dlen)
505 jsr ReadAndCheckError ; Bails out in case of errors
507 ; We've got the code and data segments in memory. Next section contains
508 ; undefined references which we don't support. So check if the count of
509 ; undefined references is actually zero.
515 Undef: jmp FormatError
517 ; Number of undefined references was zero. Next come the relocation tables
518 ; for code and data segment. Relocate the code segment
521 ldx Module + 1 ; Code segment address
524 ; Relocate the data segment
527 add Header + O65_HDR::TLEN
530 adc Header + O65_HDR::TLEN + 1
532 pla ; Data segment address in a/x
535 ; We're done. Restore the register bank and return a success code
537 jsr RestoreRegBank ; X will be zero on return