aboutsummaryrefslogtreecommitdiff
path: root/reloc.s
blob: ed22f13431f97c98c31edaf6e95aa016ee4adc80 (plain)
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
;;; reloc.s - relocate code. this is just one puzzle piece.
;;; see README.txt for details.

 .export _main
 .include "atari.inc"

 start_addr = $71c0

 ; mkrelocxex.c appends this stuff.
 code_start = end_addr
 code_end   = end_addr+2
 code_run   = end_addr+4
 code_init  = end_addr+6
 table      = end_addr+8

 ; we use 9 bytes of zero page, in the floating-point area.
 zp_addr      = FR0
 offset_pages = zp_addr   ; 1 byte
 table_ptr    = zp_addr+1 ; 2 bytes
 dest_ptr     = zp_addr+3 ; 2 bytes
 code_ptr     = zp_addr+5 ; 2 bytes
 shfreg       = zp_addr+7 ; 1 byte
 pagecount    = zp_addr+8 ; 1 byte

 ; "manually" create the atari binary load header.
 .org start_addr - 6
 .word $ffff
 .word start_addr
 .word end_addr - 1

_main:
 ; if MEMLO isn't on a page boundary, move it up to the next
 ; page, e.g. $1cfc => $1d00.
 lda MEMLO
 beq calc_offset
 inc MEMLO+1
 lda #0
 sta MEMLO

calc_offset:
 lda code_start+1
 sec
 sbc MEMLO+1
 sta offset_pages

 bcs relocate_code

whoops:
 ; whoops, MEMLO is too high!
freeze: bcc freeze ; wait for the user to press Reset.

relocate_code:
 ; adjust addresses while moving the code.
 ; point to the relocation table...
 lda #<table
 sta table_ptr
 lda #>table
 sta table_ptr+1

 ; ...and to the code we're moving...
 lda code_start
 sta code_ptr
 lda code_start+1
 sta code_ptr+1

 ; ...and to the destination we're moving to.
 lda MEMLO
 sta dest_ptr
 lda MEMLO+1
 sta dest_ptr+1

 ; pagecount = (code_end >> 8) - (code_start >> 8) + 2
 lda code_end+1
 sec
 sbc code_start+1
 tax
 inx
 inx
 stx pagecount

 ldx #0
 lda (table_ptr,x)
 sta shfreg

 ; this moves a page at a time, meaning if code_end isn't
 ; on an even page boundary, we move a little more than
 ; needed. it won't hurt anything, and it follows the
 ; KISS principle.
 ldy #0
move_loop:
 cpx #8
 bne shiftit

 ; bump bitmap pointer
 inc table_ptr
 bne tpok
 inc table_ptr+1

tpok:
 ; get next byte of bitmap
 ldx #0
 lda (table_ptr,x)
 sta shfreg

shiftit:
 ; get next bit from bitmap byte
 inx
 lda (code_ptr),y
 asl shfreg
 bcc store_byte
 sbc offset_pages

store_byte:
 ; store the (possibly adjusted) byte at the destination...
 sta (dest_ptr),y

 iny
 bne move_loop
 inc code_ptr+1
 inc dest_ptr+1
 dec pagecount
 bne move_loop
 ;;; end of relocate_code

 ; bump MEMLO to point one byte past the end of the moved code.
 lda code_end
 sec
 sbc code_start
 sta code_end
 lda code_end+1
 sbc code_start+1
 sta code_end+1
 lda code_end
 sec         ; add 1 extra (we want code len + 1)
 adc MEMLO
 sta MEMLO
 lda code_end+1
 adc MEMLO+1
 sta MEMLO+1 ; MEMLO now MEMLO + code length + 1 byte

 ; do we have a run address?
 lda code_run+1
 beq do_init ; no: $00xx not valid.

 ; point RUNAD at adjusted run address.
 sec
 sbc offset_pages
 sta RUNAD+1
 lda code_run
 sta RUNAD

do_init:
 ; if there's an init address, call it (just like DOS would).
 lda code_init+1
 beq done          ; if hi byte is 0, assume lo byte is also 0.

 sec
 sbc offset_pages  ; call at relocated location, of course.
 sta code_init+1

 jmp (code_init)

 ; done
done:
 rts

end_addr:

 .out .sprintf("reloc.s code size $%04x (%d)", (* - start_addr), (* - start_addr))

; this was for testing only. mkrelocxex.c adds the init address.
; .word INITAD
; .word INITAD+1
; .word _main