blob: 6fa4a2cc7c461a8c203a6df3f959546b9d609e8f (
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
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
|
\ tag: forth memory allocation
\
\ Copyright (C) 2002-2003 Stefan Reinauer
\
\ See the file "COPYING" for further information about
\ the copyright and warranty status of this work.
\
\ 7.3.3.2 memory allocation
\ these need to be initialized by the forth kernel by now.
variable start-mem 0 start-mem ! \ start of memory
variable end-mem 0 end-mem ! \ end of memory
variable free-list 0 free-list ! \ free list head
\ initialize necessary variables and write a valid
\ free-list entry containing all of the memory.
\ start-mem: pointer to start of memory.
\ end-mem: pointer to end of memory.
\ free-list: head of linked free list
: init-mem ( start-addr size )
over dup
start-mem ! \ write start-mem
free-list ! \ write first freelist entry
2dup /n - swap ! \ write 'len' entry
over cell+ 0 swap ! \ write 'next' entry
+ end-mem ! \ write end-mem
;
\ --------------------------------------------------------------------
\ return pointer to smallest free block that contains
\ at least nb bytes and the block previous the the
\ actual block. On failure the pointer to the smallest
\ free block is 0.
: smallest-free-block ( nb -- prev ptr | 0 0 )
0 free-list @
fffffff 0 0 >r >r >r
begin
dup
while
( nb prev pp R: best_nb best_pp )
dup @ 3 pick r@ within if
( nb prev pp )
r> r> r> 3drop \ drop old smallest
2dup >r >r dup @ >r \ new smallest
then
nip dup \ prev = pp
cell + @ \ pp = pp->next
repeat
3drop r> drop r> r>
;
\ --------------------------------------------------------------------
\ allocate size bytes of memory
\ return pointer to memory (or throws an exception on failure).
: alloc-mem ( size -- addr )
\ make it legal (and fast) to allocate 0 bytes
dup 0= if exit then
aligned \ keep memory aligned.
dup smallest-free-block \ look up smallest free block.
dup 0= if
\ 2drop
-15 throw \ out of memory
then
( al-size prev addr )
\ If the smallest fitting block found is bigger than
\ the size of the requested block plus 2*cellsize we
\ can split the block in 2 parts. otherwise return a
\ slightly bigger block than requested.
dup @ ( d->len ) 3 pick cell+ cell+ > if
\ splitting the block in 2 pieces.
\ new block = old block + len field + size of requested mem
dup 3 pick cell+ + ( al-size prev addr nd )
\ new block len = old block len - req. mem size - 1 cell
over @ ( al-size prev addr nd addr->len )
4 pick ( ... al-size )
cell+ - ( al-size prev addr nd nd nd->len )
over ! ( al-size prev addr nd )
over cell+ @ ( al-size prev addr nd addr->next )
\ write addr->next to nd->next
over cell+ ! ( al-size prev addr nd )
over 4 pick swap !
else
\ don't split the block, it's too small.
dup cell+ @
then
( al-size prev addr nd )
\ If the free block we got is the first one rewrite free-list
\ pointer instead of the previous entry's next field.
rot dup 0= if drop free-list else cell+ then
( al-size addr nd prev->next|fl )
!
nip cell+ \ remove al-size and skip len field of returned pointer
;
\ --------------------------------------------------------------------
\ free block given by addr. The length of the
\ given block is stored at addr - cellsize.
\
\ merge with blocks to the left and right
\ immediately, if they are free.
: free-mem ( addr len -- )
\ we define that it is legal to free 0-byte areas
0= if drop exit then
( addr )
\ check if the address to free is somewhere within
\ our available memory. This fails badly on discontigmem
\ architectures. If we need more RAM than fits on one
\ contiguous memory area we are too bloated anyways. ;)
dup start-mem @ end-mem @ within 0= if
\ ." free-mem: no such memory: 0x" u. cr
exit
then
/n - \ get real block address
0 free-list @ ( addr prev l )
begin \ now scan the free list
dup 0<> if \ only check len, if block ptr != 0
dup dup @ cell+ + 3 pick <
else
false
then
while
nip dup \ prev=l
cell+ @ \ l=l->next
repeat
( addr prev l )
dup 0<> if \ do we have free memory to merge with?
dup dup @ cell+ + 3 pick = if \ hole hit. adding bytes.
\ freeaddr = end of current block -> merge
( addr prev l )
rot @ cell+ ( prev l f->len+cellsize )
over @ + \ add l->len
over ! ( prev l )
swap over cell+ @ \ f = l; l = l->next;
\ The free list is sorted by addresses. When merging at the
\ start of our block we might also want to merge at the end
\ of it. Therefore we fall through to the next border check
\ instead of returning.
true \ fallthrough value
else
false \ no fallthrough
then
>r \ store fallthrough on ret stack
( addr prev l )
dup 3 pick dup @ cell+ + = if \ hole hit. real merging.
\ current block starts where block to free ends.
\ end of free block addr = current block -> merge and exit
( addr prev l )
2 pick dup @ ( f f->len )
2 pick @ cell+ + ( f newlen )
swap ! ( addr prev l )
3dup drop
0= if
free-list
else
2 pick cell+
then ( value prev->next|free-list )
! ( addr prev l )
cell+ @ rot ( prev l->next addr )
cell+ ! drop
r> drop exit \ clean up return stack
then
r> if 3drop exit then \ fallthrough? -> exit
then
\ loose block - hang it before current.
( addr prev l )
\ hang block to free in front of the current entry.
dup 3 pick cell+ ! \ f->next = l;
free-list @ = if \ is block to free new list head?
over free-list !
then
( addr prev )
dup 0<> if \ if (prev) prev->next=f
cell+ !
else
2drop \ no fixup needed. clean up.
then
;
|