]> git.sesse.net Git - pistorm/blob - a314/a314device/fix_mem_region.c
Add code to autoconfigure A314
[pistorm] / a314 / a314device / fix_mem_region.c
1 #include <exec/types.h>
2 #include <exec/execbase.h>
3 #include <exec/memory.h>
4 #include <proto/exec.h>
5 #include <proto/graphics.h>
6
7 #include "a314.h"
8 #include "fix_mem_region.h"
9 #include "protocol.h"
10
11 struct MemChunkList
12 {
13         struct MemChunk *first;
14         struct MemChunk *last;
15         ULONG free;
16 };
17
18 void add_chunk(struct MemChunkList *l, struct MemChunk *mc)
19 {
20         if (l->first == NULL)
21                 l->first = mc;
22         else
23                 l->last->mc_Next = mc;
24         l->last = mc;
25         l->free += mc->mc_Bytes;
26 }
27
28 struct MemHeader *split_region(struct MemHeader *lower, ULONG split_at)
29 {
30         struct MemHeader *upper = (struct MemHeader *)AllocMem(sizeof(struct MemHeader), MEMF_PUBLIC | MEMF_CLEAR);
31
32         struct MemChunkList ll = {NULL, NULL, 0};
33         struct MemChunkList ul = {NULL, NULL, 0};
34
35         struct MemChunk *mc = lower->mh_First;
36
37         while (mc != NULL)
38         {
39                 struct MemChunk *next_chunk = mc->mc_Next;
40                 mc->mc_Next = NULL;
41
42                 ULONG start = (ULONG)mc;
43                 ULONG end = start + mc->mc_Bytes;
44
45                 if (end <= split_at)
46                         add_chunk(&ll, mc);
47                 else if (split_at <= start)
48                         add_chunk(&ul, mc);
49                 else
50                 {
51                         mc->mc_Bytes = split_at - start;
52                         add_chunk(&ll, mc);
53
54                         struct MemChunk *new_chunk = (struct MemChunk *)split_at;
55                         new_chunk->mc_Next = NULL;
56                         new_chunk->mc_Bytes = end - split_at;
57                         add_chunk(&ul, new_chunk);
58                 }
59                 mc = next_chunk;
60         }
61
62         upper->mh_Node.ln_Type = NT_MEMORY;
63         upper->mh_Node.ln_Pri = lower->mh_Node.ln_Pri;
64         upper->mh_Node.ln_Name = lower->mh_Node.ln_Name; // Use a custom name?
65         upper->mh_Attributes = lower->mh_Attributes;
66
67         lower->mh_First = ll.first;
68         upper->mh_First = ul.first;
69
70         upper->mh_Lower = (APTR)split_at;
71         upper->mh_Upper = lower->mh_Upper;
72         lower->mh_Upper = (APTR)split_at;
73
74         lower->mh_Free = ll.free;
75         upper->mh_Free = ul.free;
76
77         return upper;
78 }
79
80 BOOL overlap(struct MemHeader *mh, ULONG lower, ULONG upper)
81 {
82         return lower < (ULONG)(mh->mh_Upper) && (ULONG)(mh->mh_Lower) < upper;
83 }
84
85 void mark_region_a314(ULONG address, ULONG size)
86 {
87         struct List *memlist = &(SysBase->MemList);
88
89         for (struct Node *node = memlist->lh_Head; node->ln_Succ != NULL; node = node->ln_Succ)
90         {
91                 struct MemHeader *mh = (struct MemHeader *)node;
92                 if (overlap(mh, address, address + size))
93                 {
94                         if ((ULONG)mh->mh_Lower < address)
95                         {
96                                 mh->mh_Attributes &= ~MEMF_A314;
97                                 mh = split_region(mh, address);
98                         }
99                         else
100                                 Remove((struct Node *)mh);
101
102                         if (address + size < (ULONG)mh->mh_Upper)
103                         {
104                                 struct MemHeader *new_mh = split_region(mh, address + size);
105                                 new_mh->mh_Attributes &= ~MEMF_A314;
106                                 Enqueue(memlist, (struct Node *)new_mh);
107                         }
108
109                         mh->mh_Attributes |= MEMF_A314;
110                         Enqueue(memlist, (struct Node *)mh);
111                         return;
112                 }
113         }
114 }
115
116 BOOL fix_memory()
117 {
118         Forbid();
119         mark_region_a314(ca->mem_base, ca->mem_size);
120         Permit();
121         return TRUE;
122 }
123
124 ULONG translate_address_a314(__reg("a0") void *address)
125 {
126         ULONG offset = (ULONG)address - ca->mem_base;
127         if (offset < ca->mem_size)
128                 return offset;
129         return -1;
130 }