comment change
[external/binutils.git] / gdb / dcache.c
1 /* Caching code.  Typically used by remote back ends for
2    caching remote memory.
3
4    Copyright 1992, 1993 Free Software Foundation, Inc.
5
6 This file is part of GDB.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
21
22 #include "defs.h"
23 #include "dcache.h"
24
25 extern int insque();
26 extern int remque();
27
28 /* The data cache records all the data read from the remote machine
29    since the last time it stopped.
30
31    Each cache block holds LINE_SIZE bytes of data
32    starting at a multiple-of-LINE_SIZE address.  */
33
34 #define LINE_SIZE_MASK ((LINE_SIZE - 1))        /* eg 7*2+1= 111*/
35 #define XFORM(x)  (((x) & LINE_SIZE_MASK) >> 2)
36
37 /* Free all the data cache blocks, thus discarding all cached data.  */
38 void
39 dcache_flush (dcache)
40      DCACHE *dcache;
41 {
42   register struct dcache_block *db;
43
44   while ((db = dcache->dcache_valid.next) != &dcache->dcache_valid)
45     {
46       remque (db);
47       insque (db, &dcache->dcache_free);
48     }
49 }
50
51 /*
52  * If addr is present in the dcache, return the address of the block
53  * containing it.
54  */
55 struct dcache_block *
56 dcache_hit (dcache, addr)
57      DCACHE *dcache;
58      unsigned int addr;
59 {
60   register struct dcache_block *db;
61
62   if (addr & 3)
63     abort ();
64
65   /* Search all cache blocks for one that is at this address.  */
66   db = dcache->dcache_valid.next;
67   while (db != &dcache->dcache_valid)
68     {
69       if ((addr & ~LINE_SIZE_MASK) == db->addr)
70         return db;
71       db = db->next;
72     }
73   return NULL;
74 }
75
76 /*  Return the int data at address ADDR in dcache block DC.  */
77 int
78 dcache_value (db, addr)
79      struct dcache_block *db;
80      unsigned int addr;
81 {
82   if (addr & 3)
83     abort ();
84   return (db->data[XFORM (addr)]);
85 }
86
87 /* Get a free cache block, put or keep it on the valid list,
88    and return its address.  The caller should store into the block
89    the address and data that it describes, then remque it from the
90    free list and insert it into the valid list.  This procedure
91    prevents errors from creeping in if a memory retrieval is
92    interrupted (which used to put garbage blocks in the valid
93    list...).  */
94 struct dcache_block *
95 dcache_alloc (dcache)
96      DCACHE *dcache;
97 {
98   register struct dcache_block *db;
99
100   if ((db = dcache->dcache_free.next) == &dcache->dcache_free)
101     {
102       /* If we can't get one from the free list, take last valid and put
103          it on the free list.  */
104       db = dcache->dcache_valid.last;
105       remque (db);
106       insque (db, &dcache->dcache_free);
107     }
108
109   remque (db);
110   insque (db, &dcache->dcache_valid);
111   return (db);
112 }
113
114 /* Return the contents of the word at address ADDR in the remote machine,
115    using the data cache.  */
116 int
117 dcache_fetch (dcache, addr)
118      DCACHE *dcache;
119      CORE_ADDR addr;
120 {
121   register struct dcache_block *db;
122
123   db = dcache_hit (dcache, addr);
124   if (db == 0)
125     {
126       db = dcache_alloc (dcache);
127       immediate_quit++;
128       (*dcache->read_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
129       immediate_quit--;
130       db->addr = addr & ~LINE_SIZE_MASK;
131       remque (db);              /* Off the free list */
132       insque (db, &dcache->dcache_valid);       /* On the valid list */
133     }
134   return (dcache_value (db, addr));
135 }
136
137 /* Write the word at ADDR both in the data cache and in the remote machine.  */
138 void
139 dcache_poke (dcache, addr, data)
140      DCACHE *dcache;
141      CORE_ADDR addr;
142      int data;
143 {
144   register struct dcache_block *db;
145
146   /* First make sure the word is IN the cache.  DB is its cache block.  */
147   db = dcache_hit (dcache, addr);
148   if (db == 0)
149     {
150       db = dcache_alloc (dcache);
151       immediate_quit++;
152       (*dcache->write_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
153       immediate_quit--;
154       db->addr = addr & ~LINE_SIZE_MASK;
155       remque (db);              /* Off the free list */
156       insque (db, &dcache->dcache_valid);       /* On the valid list */
157     }
158
159   /* Modify the word in the cache.  */
160   db->data[XFORM (addr)] = data;
161
162   /* Send the changed word.  */
163   immediate_quit++;
164   (*dcache->write_memory) (addr, (unsigned char *) &data, 4);
165   immediate_quit--;
166 }
167
168 /* Initialize the data cache.  */
169 DCACHE *
170 dcache_init (reading, writing)
171      memxferfunc reading;
172      memxferfunc writing;
173 {
174   register i;
175   register struct dcache_block *db;
176   DCACHE *dcache;
177
178   dcache = xmalloc(sizeof(*dcache));
179   dcache->read_memory = reading;
180   dcache->write_memory = writing;
181   dcache->the_cache = xmalloc(sizeof(*dcache->the_cache) * DCACHE_SIZE);
182
183   dcache->dcache_free.next = dcache->dcache_free.last = &dcache->dcache_free;
184   dcache->dcache_valid.next = dcache->dcache_valid.last = &dcache->dcache_valid;
185   for (db = dcache->the_cache, i = 0; i < DCACHE_SIZE; i++, db++)
186     insque (db, &dcache->dcache_free);
187
188   return(dcache);
189 }
190