break dcache code out of remote-bug.c
[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 ninMemGet is interrupted
92    (which used to put garbage blocks in the valid list...).  */
93 struct dcache_block *
94 dcache_alloc (dcache)
95      DCACHE *dcache;
96 {
97   register struct dcache_block *db;
98
99   if ((db = dcache->dcache_free.next) == &dcache->dcache_free)
100     {
101       /* If we can't get one from the free list, take last valid and put
102          it on the free list.  */
103       db = dcache->dcache_valid.last;
104       remque (db);
105       insque (db, &dcache->dcache_free);
106     }
107
108   remque (db);
109   insque (db, &dcache->dcache_valid);
110   return (db);
111 }
112
113 /* Return the contents of the word at address ADDR in the remote machine,
114    using the data cache.  */
115 int
116 dcache_fetch (dcache, addr)
117      DCACHE *dcache;
118      CORE_ADDR addr;
119 {
120   register struct dcache_block *db;
121
122   db = dcache_hit (dcache, addr);
123   if (db == 0)
124     {
125       db = dcache_alloc (dcache);
126       immediate_quit++;
127       (*dcache->read_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
128       immediate_quit--;
129       db->addr = addr & ~LINE_SIZE_MASK;
130       remque (db);              /* Off the free list */
131       insque (db, &dcache->dcache_valid);       /* On the valid list */
132     }
133   return (dcache_value (db, addr));
134 }
135
136 /* Write the word at ADDR both in the data cache and in the remote machine.  */
137 void
138 dcache_poke (dcache, addr, data)
139      DCACHE *dcache;
140      CORE_ADDR addr;
141      int data;
142 {
143   register struct dcache_block *db;
144
145   /* First make sure the word is IN the cache.  DB is its cache block.  */
146   db = dcache_hit (dcache, addr);
147   if (db == 0)
148     {
149       db = dcache_alloc (dcache);
150       immediate_quit++;
151       (*dcache->write_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
152       immediate_quit--;
153       db->addr = addr & ~LINE_SIZE_MASK;
154       remque (db);              /* Off the free list */
155       insque (db, &dcache->dcache_valid);       /* On the valid list */
156     }
157
158   /* Modify the word in the cache.  */
159   db->data[XFORM (addr)] = data;
160
161   /* Send the changed word.  */
162   immediate_quit++;
163   (*dcache->write_memory) (addr, (unsigned char *) &data, 4);
164   immediate_quit--;
165 }
166
167 /* Initialize the data cache.  */
168 DCACHE *
169 dcache_init (reading, writing)
170      memxferfunc reading;
171      memxferfunc writing;
172 {
173   register i;
174   register struct dcache_block *db;
175   DCACHE *dcache;
176
177   dcache = xmalloc(sizeof(*dcache));
178   dcache->read_memory = reading;
179   dcache->write_memory = writing;
180   dcache->the_cache = xmalloc(sizeof(*dcache->the_cache) * DCACHE_SIZE);
181
182   dcache->dcache_free.next = dcache->dcache_free.last = &dcache->dcache_free;
183   dcache->dcache_valid.next = dcache->dcache_valid.last = &dcache->dcache_valid;
184   for (db = dcache->the_cache, i = 0; i < DCACHE_SIZE; i++, db++)
185     insque (db, &dcache->dcache_free);
186
187   return(dcache);
188 }
189