summaryrefslogtreecommitdiff
path: root/v2.0/source/ALLOC.ASM
blob: 7b698260f52a9ae4a0af28d72219c5b2835ca3ed (plain) (blame)
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
;
; xenix memory calls for MSDOS
;
; CAUTION: The following routines rely on the fact that arena_signature and
; arena_owner_system are all equal to zero and are contained in DI.
;
INCLUDE DOSSEG.ASM

CODE    SEGMENT BYTE PUBLIC  'CODE'
        ASSUME  SS:DOSGROUP,CS:DOSGROUP

.xlist
.xcref
INCLUDE DOSSYM.ASM
INCLUDE DEVSYM.ASM
.cref
.list

TITLE ALLOC.ASM - memory arena manager
NAME Alloc

SUBTTL memory allocation utility routines
PAGE
;
; arena data
;
        i_need  arena_head,WORD         ; seg address of start of arena
        i_need  CurrentPDB,WORD         ; current process data block addr
        i_need  FirstArena,WORD         ; first free block found
        i_need  BestArena,WORD          ; best free block found
        i_need  LastArena,WORD          ; last free block found
        i_need  AllocMethod,BYTE        ; how to alloc first(best)last

;
; arena_free_process
; input:    BX - PID of process
; output:   free all blocks allocated to that PID
;
        procedure   arena_free_process,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        MOV     DI,arena_signature
        MOV     AX,[arena_head]
        CALL    Check_Signature         ; ES <- AX, check for valid block

arena_free_process_loop:
        retc
        PUSH    ES
        POP     DS
        CMP     DS:[arena_owner],BX     ; is block owned by pid?
        JNZ     arena_free_next         ; no, skip to next
        MOV     DS:[arena_owner],DI     ; yes... free him

arena_free_next:
        CMP     BYTE PTR DS:[DI],arena_signature_end
                                        ; end of road, Jack?
        retz                            ; never come back no more
        CALL    arena_next              ; next item in ES/AX carry set if trash
        JMP     arena_free_process_loop

arena_free_process  ENDP

;
; arena_next
; input:    DS - pointer to block head
; output:   AX,ES - pointers to next head
;           carry set if trashed arena
;
        procedure   arena_next,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        MOV     AX,DS                   ; AX <- current block
        ADD     AX,DS:[arena_size]      ; AX <- AX + current block length
        INC     AX                      ; remember that header!
;
;       fall into check_signature and return
;
;       CALL    check_signature         ; ES <- AX, carry set if error
;       RET
arena_next  ENDP

;
; check_signature
; input:    AX - address of block header
; output:   ES=AX, carry set if signature is bad
;
        procedure   check_signature,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        MOV     ES,AX                   ; ES <- AX
        CMP     BYTE PTR ES:[DI],arena_signature_normal
                                        ; IF next signature = not_end THEN
        JZ      check_signature_ok      ;   GOTO ok
        CMP     BYTE PTR ES:[DI],arena_signature_end
                                        ; IF next signature = end then
        JZ      check_signature_ok      ;   GOTO ok
        STC                             ; set error
        return

check_signature_ok:
        CLC
        return
Check_signature ENDP

;
; Coalesce - combine free blocks ahead with current block
; input:    DS - pointer to head of free block
; output:   updated head of block, AX is next block
;           carry set -> trashed arena
;
        procedure   Coalesce,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        CMP     BYTE PTR DS:[DI],arena_signature_end
                                        ; IF current signature = END THEN
        retz                            ;   GOTO ok
        CALL    arena_next              ; ES, AX <- next block, Carry set if error
        retc                            ; IF no error THEN GOTO check

coalesce_check:
        CMP     ES:[arena_owner],DI
        retnz                           ; IF next block isnt free THEN return
        MOV     CX,ES:[arena_size]      ; CX <- next block size
        INC     CX                      ; CX <- CX + 1 (for header size)
        ADD     DS:[arena_size],CX      ; current size <- current size + CX
        MOV     CL,ES:[DI]              ; move up signature
        MOV     DS:[DI],CL
        JMP     coalesce                ; try again
Coalesce    ENDP

SUBTTL $Alloc - allocate space in memory
PAGE
;
;   Assembler usage:
;           MOV     BX,size
;           MOV     AH,Alloc
;           INT     21h
;         AX:0 is pointer to allocated memory
;         BX is max size if not enough memory
;
;   Description:
;           Alloc returns  a  pointer  to  a  free  block of
;       memory that has the requested  size  in  paragraphs.
;
;   Error return:
;           AX = error_not_enough_memory
;              = error_arena_trashed
;
        procedure   $ALLOC,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        XOR     AX,AX
        MOV     DI,AX

        MOV     [FirstArena],AX         ; init the options
        MOV     [BestArena],AX
        MOV     [LastArena],AX

        PUSH    AX                      ; alloc_max <- 0
        MOV     AX,[arena_head]         ; AX <- beginning of arena
        CALL    Check_signature         ; ES <- AX, carry set if error
        JC      alloc_err               ; IF error THEN GOTO err

alloc_scan:
        PUSH    ES
        POP     DS                      ; DS <- ES
        CMP     DS:[arena_owner],DI
        JZ      alloc_free              ; IF current block is free THEN examine

alloc_next:
        CMP     BYTE PTR DS:[DI],arena_signature_end
                                        ; IF current block is last THEN
        JZ      alloc_end               ;   GOTO end
        CALL    arena_next              ; AX, ES <- next block, Carry set if error
        JNC     alloc_scan              ; IF no error THEN GOTO scan

alloc_err:
        POP     AX

alloc_trashed:
        error   error_arena_trashed

alloc_end:
        CMP     [FirstArena],0
        JNZ     alloc_do_split

alloc_fail:
        invoke  get_user_stack
        POP     BX
        MOV     [SI].user_BX,BX
        error   error_not_enough_memory

alloc_free:
        CALL    coalesce                ; add following free block to current
        JC      alloc_err               ; IF error THEN GOTO err
        MOV     CX,DS:[arena_size]

        POP     DX                      ; check for max found size
        CMP     CX,DX
        JNA     alloc_test
        MOV     DX,CX

alloc_test:
        PUSH    DX
        CMP     BX,CX                   ; IF BX > size of current block THEN
        JA      alloc_next              ;   GOTO next

        CMP     [FirstArena],0
        JNZ     alloc_best
        MOV     [FirstArena],DS         ; save first one found
alloc_best:
        CMP     [BestArena],0
        JZ      alloc_make_best         ; initial best
        PUSH    ES
        MOV     ES,[BestArena]
        CMP     ES:[arena_size],CX      ; is size of best larger than found?
        POP     ES
        JBE     alloc_last
alloc_make_best:
        MOV     [BestArena],DS          ; assign best
alloc_last:
        MOV     [LastArena],DS          ; assign last
        JMP     alloc_next

;
; split the block high
;
alloc_do_split_high:
        MOV     DS,[LastArena]
        MOV     CX,DS:[arena_size]
        SUB     CX,BX
        MOV     DX,DS
        JE      alloc_set_owner         ; sizes are equal, no split
        ADD     DX,CX                   ; point to next block
        MOV     ES,DX                   ; no decrement!
        DEC     CX
        XCHG    BX,CX                   ; bx has size of lower block
        JMP     alloc_set_sizes         ; cx has upper (requested) size

;
; we have scanned memory and have found all appropriate blocks
; check for the type of allocation desired; first and best are identical
; last must be split high
;
alloc_do_split:
        CMP     BYTE PTR [AllocMethod], 1
        JA      alloc_do_split_high
        MOV     DS,[FirstArena]
        JB      alloc_get_size
        MOV     DS,[BestArena]
alloc_get_size:
        MOV     CX,DS:[arena_size]
        SUB     CX,BX                   ; get room left over
        MOV     AX,DS
        MOV     DX,AX                   ; save for owner setting
        JE      alloc_set_owner         ; IF BX = size THEN (don't split)
        ADD     AX,BX
        INC     AX                      ; remember the header
        MOV     ES,AX                   ; ES <- DS + BX (new header location)
        DEC     CX                      ; CX <- size of split block
alloc_set_sizes:
        MOV     DS:[arena_size],BX      ; current size <- BX
        MOV     ES:[arena_size],CX      ; split size <- CX
        MOV     BL,arena_signature_normal
        XCHG    BL,DS:[DI]              ; current signature <- 4D
        MOV     ES:[DI],BL              ; new block sig <- old block sig
        MOV     ES:[arena_owner],DI

alloc_set_owner:
        MOV     DS,DX
        MOV     AX,[CurrentPDB]
        MOV     DS:[arena_owner],AX
        MOV     AX,DS
        INC     AX
        POP     BX
        transfer    SYS_RET_OK

$alloc  ENDP

SUBTTL $SETBLOCK - change size of an allocated block (if possible)
PAGE
;
;   Assembler usage:
;           MOV     ES,block
;           MOV     BX,newsize
;           MOV     AH,setblock
;           INT     21h
;         if setblock fails for growing, BX will have the maximum
;         size possible
;   Error return:
;           AX = error_invalid_block
;              = error_arena_trashed
;              = error_not_enough_memory
;              = error_invalid_function
;
        procedure   $SETBLOCK,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        MOV     DI,arena_signature
        MOV     AX,ES
        DEC     AX
        CALL    check_signature
        JNC     setblock_grab

setblock_bad:
        JMP     alloc_trashed

setblock_grab:
        MOV     DS,AX
        CALL    coalesce
        JC      setblock_bad
        MOV     CX,DS:[arena_size]
        PUSH    CX
        CMP     BX,CX
        JBE     alloc_get_size
        JMP     alloc_fail
$setblock   ENDP

SUBTTL $DEALLOC - free previously allocated piece of memory
PAGE
;
;   Assembler usage:
;           MOV     ES,block
;           MOV     AH,dealloc
;           INT     21h
;
;   Error return:
;           AX = error_invalid_block
;              = error_arena_trashed
;
        procedure   $DEALLOC,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        MOV     DI,arena_signature
        MOV     AX,ES
        DEC     AX
        CALL    check_signature
        JC      dealloc_err
        MOV     ES:[arena_owner],DI
        transfer    SYS_RET_OK

dealloc_err:
        error   error_invalid_block
$DEALLOC    ENDP

SUBTTL $AllocOper - get/set allocation mechanism
PAGE
;
;   Assembler usage:
;           MOV     AH,AllocOper
;           MOV     BX,method
;           MOV     AL,func
;           INT     21h
;
;   Error return:
;           AX = error_invalid_function
;
        procedure   $AllocOper,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING
        CMP     AL,1
        JB      AllocOperGet
        JZ      AllocOperSet
        error   error_invalid_function
AllocOperGet:
        MOV     AL,BYTE PTR [AllocMethod]
        XOR     AH,AH
        transfer    SYS_RET_OK
AllocOperSet:
        MOV     [AllocMethod],BL
        transfer    SYS_RET_OK
$AllocOper  ENDP

do_ext

CODE    ENDS
    END