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"
39 .import pushax, pusha0, push0, push1, decax1
40 .import _malloc, _free, _bzero
41 .import __ZP_START__ ; Linker generated
42 .importzp sp, ptr1, tmp1, regbank
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 6 ; 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: .res O65_HDR_SIZE ; 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 $01, $00 ; non C64 marker
80 .byte $6F, $36, $35 ; Magic ("o65")
82 .word O65_CPU_6502|O65_RELOC_BYTE|O65_SIZE_16BIT|O65_FTYPE_EXE|O65_ADDR_SIMPLE|O65_ALIGN_1
84 ExpectedHdrSize = * - ExpectedHdr
87 ;------------------------------------------------------------------------------
88 ; PushCallerData: Push the callerdata member from control structure onto the
93 ldy #MODCTRL_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
164 ; Check the return code and bail out in case of problems
170 @L1: lda #MLOAD_ERR_READ
175 @L2: lda InputByte ; If called ReadByte, load the byte read
178 ;------------------------------------------------------------------------------
179 ; FormatError: Bail out with an o65 format error
184 ; bne CleanupAndExit ; Branch always
186 ;------------------------------------------------------------------------------
187 ; CleanupAndExit: Free any allocated resources, restore the stack and return
193 ; Restore the stack so we may return to the caller from here
198 ; Save the error return code
202 ; Check if we have to free the allocated block
206 beq @L1 ; Jump if no memory allocated
210 jsr _free ; Free the allocated block
212 ; Restore the register bank
214 @L1: jsr RestoreRegBank
216 ; Restore the error code and return to the caller
218 ldx #$00 ; Load the high byte
222 ;------------------------------------------------------------------------------
223 ; RelocSeg: Relocate the segment pointed to by a/x
227 jsr decax1 ; Start value is segment-1
231 Loop: jsr ReadByte ; Read byte from relocation table
232 beq Done ; Bail out if end of table reached
234 cmp #255 ; Special offset?
237 ; Increment offset by 254 and continue
246 ; Increment offset by A
253 ; Read the relocation byte, extract the segment id, fetch the corresponding
254 ; relocation value and place it into ptr1
262 ; Get the relocation byte again, this time extract the relocation type.
267 ; Check for and handle the different relocation types.
276 ; Relocate the low byte
284 ; Relocate a high byte
287 jsr ReadByte ; Read low byte from relocation table
290 adc RelocVal ; We just need the carry
296 jmp Loop ; Done, next entry
307 bne AddHigh ; Branch always (add high byte)
309 ;------------------------------------------------------------------------------
310 ; mod_load: Load and relocate an o65 module
315 ; Save the register bank and clear the Module pointer
327 ; Save the passed parameter
332 ; Save the stack pointer so we can bail out even from subroutines
337 ; Get the read function pointer from the control structure and place it into
347 ; Read the o65 header: C->read (C->callerdata, &H, sizeof (H))
354 ldx #0 ; Always less than 256
355 jsr ReadAndCheckError ; Bails out in case of errors
357 ; We read the o65 header successfully. Validate it.
359 ldy #ExpectedHdrSize-1
367 ; Header is ok as far as we can say now. Read all options, check for the
368 ; OS option and ignore all others. The OS option contains a version number
369 ; and the module id as additional data.
372 sty TPtr+1 ; Flag for OS option read
373 Opt: jsr ReadByte ; Read the length byte
374 beq OptDone ; Jump if done
375 sta TPtr ; Use TPtr as a counter
377 ; An option has a length of at least 2 bytes
380 bcc HeaderError ; Must be 2 bytes total at least
382 ; Check for the OS option
385 jsr ReadByte ; Get the option type
386 cmp #O65_OPT_OS ; OS option?
387 bne SkipOpt ; No: Skip
389 lda TPtr ; Get remaining length+1
390 cmp #5 ; CC65 has 6 bytes total
393 jsr ReadByte ; Get the operating system
394 cmp #O65_OS_CC65_MODULE
395 bne OSError ; Wrong operating system
397 jsr ReadByte ; Get the version number, expect zero
398 bne OSError ; Wrong version
400 jsr ReadByte ; Get low byte of id
401 ldy #MODCTRL_MODULE_ID
404 ldy #MODCTRL_MODULE_ID+1
407 inc TPtr+1 ; Remember that we got the OS
415 beq Opt ; Next option
416 jsr ReadByte ; Skip one byte
419 ; Operating system error
425 ; Options done, check that we got the OS option
431 ; Entry point for header errors
437 ; Skipped all options. Calculate the size of text+data and of text+data+bss
438 ; (the latter is the size of the memory block we need). We will store the
439 ; total module size also into the control structure for evaluation by the
443 lda Header + O65_HDR_TLEN
444 add Header + O65_HDR_DLEN
446 lda Header + O65_HDR_TLEN + 1
447 adc Header + O65_HDR_DLEN + 1
450 add Header + O65_HDR_BLEN
451 pha ; Save low byte of total size
452 ldy #MODCTRL_MODULE_SIZE
455 adc Header + O65_HDR_BLEN + 1
459 pla ; Restore low byte of total size
461 ; Total memory size is now in a/x. Allocate memory and remember the result,
462 ; both, locally and in the control structure so it the caller can access
463 ; the memory block. After that, check if we got the requested memory.
477 ; Could not allocate memory
482 ; Control structure is complete now. Clear the bss segment.
483 ; bzero (bss_addr, bss_size)
489 adc TPtr+1 ; Module + tlen + dlen
493 lda Header + O65_HDR_BLEN
494 ldx Header + O65_HDR_BLEN+1
495 jsr _bzero ; bzero (bss, bss_size);
497 ; Load code and data segment into memory. The sum of the sizes of
498 ; code+data segment is still in TPtr.
499 ; C->read (C->callerdata, C->module, H.tlen + H.dlen)
507 jsr ReadAndCheckError ; Bails out in case of errors
509 ; We've got the code and data segments in memory. Next section contains
510 ; undefined references which we don't support. So check if the count of
511 ; undefined references is actually zero.
517 Undef: jmp FormatError
519 ; Number of undefined references was zero. Next come the relocation tables
520 ; for code and data segment. Relocate the code segment
523 ldx Module + 1 ; Code segment address
526 ; Relocate the data segment
529 add Header + O65_HDR_TLEN
532 adc Header + O65_HDR_TLEN + 1
534 pla ; Data segment address in a/x
537 ; We're done. Restore the register bank and return a success code
539 jsr RestoreRegBank ; X will be zero on return