]> git.sesse.net Git - pistorm/blob - emulator.c
Add command line switch to disable Gayle emulation
[pistorm] / emulator.c
1 #include <assert.h>
2 #include <dirent.h>
3 #include <endian.h>
4 #include <fcntl.h>
5 #include <pthread.h>
6 #include <sched.h>
7 #include <signal.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include "Gayle.h"
17 #include "ide.h"
18 #include "m68k.h"
19 #include "main.h"
20
21 //#define BCM2708_PERI_BASE        0x20000000  //pi0-1
22 //#define BCM2708_PERI_BASE     0xFE000000     //pi4
23 #define BCM2708_PERI_BASE 0x3F000000  // pi3
24 #define BCM2708_PERI_SIZE 0x01000000
25 #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
26 #define GPCLK_BASE (BCM2708_PERI_BASE + 0x101000)
27 #define GPIO_ADDR 0x200000 /* GPIO controller */
28 #define GPCLK_ADDR 0x101000
29 #define CLK_PASSWD 0x5a000000
30 #define CLK_GP0_CTL 0x070
31 #define CLK_GP0_DIV 0x074
32
33 #define SA0 5
34 #define SA1 3
35 #define SA2 2
36
37 #define STATUSREGADDR  \
38   GPIO_CLR = 1 << SA0; \
39   GPIO_CLR = 1 << SA1; \
40   GPIO_SET = 1 << SA2;
41 #define W16            \
42   GPIO_CLR = 1 << SA0; \
43   GPIO_CLR = 1 << SA1; \
44   GPIO_CLR = 1 << SA2;
45 #define R16            \
46   GPIO_SET = 1 << SA0; \
47   GPIO_CLR = 1 << SA1; \
48   GPIO_CLR = 1 << SA2;
49 #define W8             \
50   GPIO_CLR = 1 << SA0; \
51   GPIO_SET = 1 << SA1; \
52   GPIO_CLR = 1 << SA2;
53 #define R8             \
54   GPIO_SET = 1 << SA0; \
55   GPIO_SET = 1 << SA1; \
56   GPIO_CLR = 1 << SA2;
57
58 #define PAGE_SIZE (4 * 1024)
59 #define BLOCK_SIZE (4 * 1024)
60
61 #define GPIOSET(no, ishigh) \
62   do {                      \
63     if (ishigh)             \
64       set |= (1 << (no));   \
65     else                    \
66       reset |= (1 << (no)); \
67   } while (0)
68
69 #define FASTBASE 0x07FFFFFF
70 #define FASTSIZE 0xFFFFFFF
71 #define GAYLEBASE 0xD80000  // D7FFFF
72 #define GAYLESIZE 0x6FFFF
73
74 #define KICKBASE 0xF80000
75 #define KICKSIZE 0x7FFFF
76
77 int mem_fd;
78 int mem_fd_gpclk;
79 int gayle_emulation_enabled = 1;
80 void *gpio_map;
81 void *gpclk_map;
82
83 // I/O access
84 volatile unsigned int *gpio;
85 volatile unsigned int *gpclk;
86 volatile unsigned int gpfsel0;
87 volatile unsigned int gpfsel1;
88 volatile unsigned int gpfsel2;
89 volatile unsigned int gpfsel0_o;
90 volatile unsigned int gpfsel1_o;
91 volatile unsigned int gpfsel2_o;
92
93 // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or
94 // SET_GPIO_ALT(x,y)
95 #define INP_GPIO(g) *(gpio + ((g) / 10)) &= ~(7 << (((g) % 10) * 3))
96 #define OUT_GPIO(g) *(gpio + ((g) / 10)) |= (1 << (((g) % 10) * 3))
97 #define SET_GPIO_ALT(g, a)  \
98   *(gpio + (((g) / 10))) |= \
99       (((a) <= 3 ? (a) + 4 : (a) == 4 ? 3 : 2) << (((g) % 10) * 3))
100
101 #define GPIO_SET \
102   *(gpio + 7)  // sets   bits which are 1 ignores bits which are 0
103 #define GPIO_CLR \
104   *(gpio + 10)  // clears bits which are 1 ignores bits which are 0
105
106 #define GET_GPIO(g) (*(gpio + 13) & (1 << g))  // 0 if LOW, (1<<g) if HIGH
107
108 #define GPIO_PULL *(gpio + 37)      // Pull up/pull down
109 #define GPIO_PULLCLK0 *(gpio + 38)  // Pull up/pull down clock
110
111 void setup_io();
112
113 uint32_t read8(uint32_t address);
114 void write8(uint32_t address, uint32_t data);
115
116 uint32_t read16(uint32_t address);
117 void write16(uint32_t address, uint32_t data);
118
119 void write32(uint32_t address, uint32_t data);
120 uint32_t read32(uint32_t address);
121
122 uint16_t read_reg(void);
123 void write_reg(unsigned int value);
124
125 volatile uint16_t srdata;
126 volatile uint32_t srdata2;
127 volatile uint32_t srdata2_old;
128
129 unsigned char g_kick[524288];
130 unsigned char g_ram[FASTSIZE + 1]; /* RAM */
131 unsigned char toggle;
132 static volatile unsigned char ovl;
133 static volatile unsigned char maprom;
134
135 void sigint_handler(int sig_num) {
136   printf("\n Exit Ctrl+C %d\n", sig_num);
137   exit(0);
138 }
139
140 void *iplThread(void *args) {
141   printf("thread!/n");
142
143   while (42) {
144     usleep(1);
145   }
146 }
147
148 int main(int argc, char *argv[]) {
149   int g;
150   const struct sched_param priority = {99};
151
152   // Some command line switch stuffles
153   for (g = 1; g < argc; g++) {
154     if (strcmp(argv[g], "--disable-gayle") == 0) {
155       gayle_emulation_enabled = 0;
156     }
157   }
158
159   sched_setscheduler(0, SCHED_FIFO, &priority);
160   mlockall(MCL_CURRENT);  // lock in memory to keep us from paging out
161
162   InitGayle();
163
164   signal(SIGINT, sigint_handler);
165   setup_io();
166
167   // Enable 200MHz CLK output on GPIO4, adjust divider and pll source depending
168   // on pi model
169   printf("Enable 200MHz GPCLK0 on GPIO4\n");
170
171   *(gpclk + (CLK_GP0_CTL / 4)) = CLK_PASSWD | (1 << 5);
172   usleep(10);
173   while ((*(gpclk + (CLK_GP0_CTL / 4))) & (1 << 7))
174     ;
175   usleep(100);
176   *(gpclk + (CLK_GP0_DIV / 4)) =
177       CLK_PASSWD | (6 << 12);  // divider , 6=200MHz on pi3
178   usleep(10);
179   *(gpclk + (CLK_GP0_CTL / 4)) =
180       CLK_PASSWD | 5 | (1 << 4);  // pll? 6=plld, 5=pllc
181   usleep(10);
182   while (((*(gpclk + (CLK_GP0_CTL / 4))) & (1 << 7)) == 0)
183     ;
184   usleep(100);
185
186   SET_GPIO_ALT(4, 0);  // gpclk0
187
188   // set SA to output
189   INP_GPIO(2);
190   OUT_GPIO(2);
191   INP_GPIO(3);
192   OUT_GPIO(3);
193   INP_GPIO(5);
194   OUT_GPIO(5);
195
196   // set gpio0 (aux0) and gpio1 (aux1) to input
197   INP_GPIO(0);
198   INP_GPIO(1);
199
200   // Set GPIO pins 6,7 and 8-23 to output
201   for (g = 6; g <= 23; g++) {
202     INP_GPIO(g);
203     OUT_GPIO(g);
204   }
205   printf("Precalculate GPIO8-23 as Output\n");
206   gpfsel0_o = *(gpio);  // store gpio ddr
207   printf("gpfsel0: %#x\n", gpfsel0_o);
208   gpfsel1_o = *(gpio + 1);  // store gpio ddr
209   printf("gpfsel1: %#x\n", gpfsel1_o);
210   gpfsel2_o = *(gpio + 2);  // store gpio ddr
211   printf("gpfsel2: %#x\n", gpfsel2_o);
212
213   // Set GPIO pins 8-23 to input
214   for (g = 8; g <= 23; g++) {
215     INP_GPIO(g);
216   }
217   printf("Precalculate GPIO8-23 as Input\n");
218   gpfsel0 = *(gpio);  // store gpio ddr
219   printf("gpfsel0: %#x\n", gpfsel0);
220   gpfsel1 = *(gpio + 1);  // store gpio ddr
221   printf("gpfsel1: %#x\n", gpfsel1);
222   gpfsel2 = *(gpio + 2);  // store gpio ddr
223   printf("gpfsel2: %#x\n", gpfsel2);
224
225   GPIO_CLR = 1 << 2;
226   GPIO_CLR = 1 << 3;
227   GPIO_SET = 1 << 5;
228
229   GPIO_SET = 1 << 6;
230   GPIO_SET = 1 << 7;
231
232   // reset cpld statemachine first
233
234   write_reg(0x01);
235   usleep(100);
236   usleep(1500);
237   write_reg(0x00);
238   usleep(100);
239
240   // load kick.rom if present
241   maprom = 1;
242   int fd = 0;
243   fd = open("kick.rom", O_RDONLY);
244   if (fd < 1) {
245     printf("Failed loading kick.rom, using motherboard kickstart\n");
246     maprom = 0;
247   } else {
248     int size = (int)lseek(fd, 0, SEEK_END);
249     if (size == 0x40000) {
250       lseek(fd, 0, SEEK_SET);
251       read(fd, &g_kick, size);
252       lseek(fd, 0, SEEK_SET);
253       read(fd, &g_kick[0x40000], size);
254     } else {
255       lseek(fd, 0, SEEK_SET);
256       read(fd, &g_kick, size);
257     }
258     printf("Loaded kick.rom with size %d kib\n", size / 1024);
259   }
260
261   // reset amiga and statemachine
262   cpu_pulse_reset();
263   ovl = 1;
264   m68k_write_memory_8(0xbfe201, 0x0001);  // AMIGA OVL
265   m68k_write_memory_8(0xbfe001, 0x0001);  // AMIGA OVL high (ROM@0x0)
266
267   usleep(1500);
268
269   m68k_init();
270   m68k_set_cpu_type(M68K_CPU_TYPE_68030);
271   m68k_pulse_reset();
272
273   if (maprom == 1) {
274     m68k_set_reg(M68K_REG_PC, 0xF80002);
275   } else {
276     m68k_set_reg(M68K_REG_PC, 0x0);
277   }
278
279   /*
280            pthread_t id;
281            int err;
282           //err = pthread_create(&id, NULL, &iplThread, NULL);
283           if (err != 0)
284               printf("\ncan't create IPL thread :[%s]", strerror(err));
285           else
286               printf("\n IPL Thread created successfully\n");
287   */
288
289   m68k_pulse_reset();
290   while (42) {
291     m68k_execute(3000);
292     if (GET_GPIO(1) == 0) {
293       srdata = read_reg();
294       m68k_set_irq((srdata >> 13) & 0xff);
295     } else {
296       m68k_set_irq(0);
297     };
298   }
299
300   return 0;
301 }
302
303 void cpu_pulse_reset(void) {
304   write_reg(0x00);
305   // printf("Status Reg%x\n",read_reg());
306   usleep(100000);
307   write_reg(0x02);
308   // printf("Status Reg%x\n",read_reg());
309 }
310
311 int cpu_irq_ack(int level) {
312   printf("cpu irq ack\n");
313   return level;
314 }
315
316 unsigned int m68k_read_memory_8(unsigned int address) {
317   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
318     return g_ram[address - FASTBASE];
319   }
320
321   if (maprom == 1) {
322     if (address > KICKBASE && address < KICKBASE + KICKSIZE) {
323       return g_kick[address - KICKBASE];
324     }
325   }
326
327   if (gayle_emulation_enabled) {
328     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
329       return readGayleB(address);
330     }
331   }
332
333   if (address < 0xffffff) {
334     return read8((uint32_t)address);
335   }
336
337   return 0;
338 }
339
340 unsigned int m68k_read_memory_16(unsigned int address) {
341   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
342     return be16toh(*(uint16_t *)&g_ram[address - FASTBASE]);
343   }
344
345   if (maprom == 1) {
346     if (address > KICKBASE && address < KICKBASE + KICKSIZE) {
347       return be16toh(*(uint16_t *)&g_kick[address - KICKBASE]);
348     }
349   }
350
351   if (gayle_emulation_enabled) {
352     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
353       return readGayle(address);
354     }
355   }
356
357   if (address < 0xffffff) {
358     return (unsigned int)read16((uint32_t)address);
359   }
360
361   return 0;
362 }
363
364 unsigned int m68k_read_memory_32(unsigned int address) {
365   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
366     return be32toh(*(uint32_t *)&g_ram[address - FASTBASE]);
367   }
368
369   if (maprom == 1) {
370     if (address > KICKBASE && address < KICKBASE + KICKSIZE) {
371       return be32toh(*(uint32_t *)&g_kick[address - KICKBASE]);
372     }
373   }
374
375   if (gayle_emulation_enabled) {
376     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
377       return readGayleL(address);
378     }
379   }
380
381   if (address < 0xffffff) {
382     uint16_t a = read16(address);
383     uint16_t b = read16(address + 2);
384     return (a << 16) | b;
385   }
386
387   return 0;
388 }
389
390 void m68k_write_memory_8(unsigned int address, unsigned int value) {
391   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
392     g_ram[address - FASTBASE] = value;
393     return;
394   }
395
396   if (gayle_emulation_enabled) {
397     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
398       writeGayleB(address, value);
399       return;
400     }
401   }
402
403   if (address == 0xbfe001) {
404     ovl = (value & (1 << 0));
405     printf("OVL:%x\n", ovl);
406   }
407
408   if (address < 0xffffff) {
409     write8((uint32_t)address, value);
410     return;
411   }
412
413   return;
414 }
415
416 void m68k_write_memory_16(unsigned int address, unsigned int value) {
417   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
418     *(uint16_t *)&g_ram[address - FASTBASE] = htobe16(value);
419     return;
420   }
421
422   if (gayle_emulation_enabled) {
423     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
424       writeGayle(address, value);
425       return;
426     }
427   }
428
429   if (address < 0xffffff) {
430     write16((uint32_t)address, value);
431     return;
432   }
433   return;
434 }
435
436 void m68k_write_memory_32(unsigned int address, unsigned int value) {
437   if (address > FASTBASE && address < FASTBASE + FASTSIZE) {
438     *(uint32_t *)&g_ram[address - FASTBASE] = htobe32(value);
439     return;
440   }
441
442   if (gayle_emulation_enabled) {
443     if (address > GAYLEBASE && address < GAYLEBASE + GAYLESIZE) {
444       writeGayleL(address, value);
445     }
446   }
447
448   if (address < 0xffffff) {
449     write16(address, value >> 16);
450     write16(address + 2, value);
451     return;
452   }
453
454   return;
455 }
456
457 void write16(uint32_t address, uint32_t data) {
458   uint32_t addr_h_s = (address & 0x0000ffff) << 8;
459   uint32_t addr_h_r = (~address & 0x0000ffff) << 8;
460   uint32_t addr_l_s = (address >> 16) << 8;
461   uint32_t addr_l_r = (~address >> 16) << 8;
462   uint32_t data_s = (data & 0x0000ffff) << 8;
463   uint32_t data_r = (~data & 0x0000ffff) << 8;
464
465   //      asm volatile ("dmb" ::: "memory");
466   W16 *(gpio) = gpfsel0_o;
467   *(gpio + 1) = gpfsel1_o;
468   *(gpio + 2) = gpfsel2_o;
469
470   *(gpio + 7) = addr_h_s;
471   *(gpio + 10) = addr_h_r;
472   GPIO_CLR = 1 << 7;
473   GPIO_SET = 1 << 7;
474
475   *(gpio + 7) = addr_l_s;
476   *(gpio + 10) = addr_l_r;
477   GPIO_CLR = 1 << 7;
478   GPIO_SET = 1 << 7;
479
480   // write phase
481   *(gpio + 7) = data_s;
482   *(gpio + 10) = data_r;
483   GPIO_CLR = 1 << 7;
484   GPIO_SET = 1 << 7;
485
486   *(gpio) = gpfsel0;
487   *(gpio + 1) = gpfsel1;
488   *(gpio + 2) = gpfsel2;
489   while ((GET_GPIO(0)))
490     ;
491   //     asm volatile ("dmb" ::: "memory");
492 }
493
494 void write8(uint32_t address, uint32_t data) {
495   if ((address & 1) == 0)
496     data = data + (data << 8);  // EVEN, A0=0,UDS
497   else
498     data = data & 0xff;  // ODD , A0=1,LDS
499   uint32_t addr_h_s = (address & 0x0000ffff) << 8;
500   uint32_t addr_h_r = (~address & 0x0000ffff) << 8;
501   uint32_t addr_l_s = (address >> 16) << 8;
502   uint32_t addr_l_r = (~address >> 16) << 8;
503   uint32_t data_s = (data & 0x0000ffff) << 8;
504   uint32_t data_r = (~data & 0x0000ffff) << 8;
505
506   //   asm volatile ("dmb" ::: "memory");
507   W8 *(gpio) = gpfsel0_o;
508   *(gpio + 1) = gpfsel1_o;
509   *(gpio + 2) = gpfsel2_o;
510
511   *(gpio + 7) = addr_h_s;
512   *(gpio + 10) = addr_h_r;
513   GPIO_CLR = 1 << 7;
514   GPIO_SET = 1 << 7;
515
516   *(gpio + 7) = addr_l_s;
517   *(gpio + 10) = addr_l_r;
518   GPIO_CLR = 1 << 7;
519   GPIO_SET = 1 << 7;
520
521   // write phase
522   *(gpio + 7) = data_s;
523   *(gpio + 10) = data_r;
524   GPIO_CLR = 1 << 7;
525   GPIO_SET = 1 << 7;
526
527   *(gpio) = gpfsel0;
528   *(gpio + 1) = gpfsel1;
529   *(gpio + 2) = gpfsel2;
530   while ((GET_GPIO(0)))
531     ;
532   //   asm volatile ("dmb" ::: "memory");
533 }
534
535 uint32_t read16(uint32_t address) {
536   volatile int val;
537   uint32_t addr_h_s = (address & 0x0000ffff) << 8;
538   uint32_t addr_h_r = (~address & 0x0000ffff) << 8;
539   uint32_t addr_l_s = (address >> 16) << 8;
540   uint32_t addr_l_r = (~address >> 16) << 8;
541
542   //   asm volatile ("dmb" ::: "memory");
543   R16
544
545       *(gpio) = gpfsel0_o;
546   *(gpio + 1) = gpfsel1_o;
547   *(gpio + 2) = gpfsel2_o;
548
549   *(gpio + 7) = addr_h_s;
550   *(gpio + 10) = addr_h_r;
551   GPIO_CLR = 1 << 7;
552   GPIO_SET = 1 << 7;
553
554   *(gpio + 7) = addr_l_s;
555   *(gpio + 10) = addr_l_r;
556   GPIO_CLR = 1 << 7;
557   GPIO_SET = 1 << 7;
558
559   // read phase
560   *(gpio) = gpfsel0;
561   *(gpio + 1) = gpfsel1;
562   *(gpio + 2) = gpfsel2;
563   GPIO_CLR = 1 << 6;
564   while (!(GET_GPIO(0)))
565     ;
566   GPIO_CLR = 1 << 6;
567   val = *(gpio + 13);
568   GPIO_SET = 1 << 6;
569   //    asm volatile ("dmb" ::: "memory");
570   return (val >> 8) & 0xffff;
571 }
572
573 uint32_t read8(uint32_t address) {
574   int val;
575   uint32_t addr_h_s = (address & 0x0000ffff) << 8;
576   uint32_t addr_h_r = (~address & 0x0000ffff) << 8;
577   uint32_t addr_l_s = (address >> 16) << 8;
578   uint32_t addr_l_r = (~address >> 16) << 8;
579
580   //    asm volatile ("dmb" ::: "memory");
581   R8 *(gpio) = gpfsel0_o;
582   *(gpio + 1) = gpfsel1_o;
583   *(gpio + 2) = gpfsel2_o;
584
585   *(gpio + 7) = addr_h_s;
586   *(gpio + 10) = addr_h_r;
587   GPIO_CLR = 1 << 7;
588   GPIO_SET = 1 << 7;
589
590   *(gpio + 7) = addr_l_s;
591   *(gpio + 10) = addr_l_r;
592   GPIO_CLR = 1 << 7;
593   GPIO_SET = 1 << 7;
594
595   // read phase
596   *(gpio) = gpfsel0;
597   *(gpio + 1) = gpfsel1;
598   *(gpio + 2) = gpfsel2;
599
600   GPIO_CLR = 1 << 6;
601   while (!(GET_GPIO(0)))
602     ;
603   GPIO_CLR = 1 << 6;
604   val = *(gpio + 13);
605   GPIO_SET = 1 << 6;
606   //    asm volatile ("dmb" ::: "memory");
607
608   val = (val >> 8) & 0xffff;
609   if ((address & 1) == 0)
610     val = (val >> 8) & 0xff;  // EVEN, A0=0,UDS
611   else
612     val = val & 0xff;  // ODD , A0=1,LDS
613   return val;
614 }
615
616 /******************************************************/
617
618 void write_reg(unsigned int value) {
619   STATUSREGADDR
620   *(gpio) = gpfsel0_o;
621   *(gpio + 1) = gpfsel1_o;
622   *(gpio + 2) = gpfsel2_o;
623   *(gpio + 7) = (value & 0xffff) << 8;
624   *(gpio + 10) = (~value & 0xffff) << 8;
625   GPIO_CLR = 1 << 7;
626   GPIO_CLR = 1 << 7;  // delay
627   GPIO_SET = 1 << 7;
628   GPIO_SET = 1 << 7;
629   // Bus HIGH-Z
630   *(gpio) = gpfsel0;
631   *(gpio + 1) = gpfsel1;
632   *(gpio + 2) = gpfsel2;
633 }
634
635 uint16_t read_reg(void) {
636   uint32_t val;
637   STATUSREGADDR
638   // Bus HIGH-Z
639   *(gpio) = gpfsel0;
640   *(gpio + 1) = gpfsel1;
641   *(gpio + 2) = gpfsel2;
642   GPIO_CLR = 1 << 6;
643   GPIO_CLR = 1 << 6;  // delay
644   GPIO_CLR = 1 << 6;
645   GPIO_CLR = 1 << 6;
646   val = *(gpio + 13);
647   GPIO_SET = 1 << 6;
648   return (uint16_t)(val >> 8);
649 }
650
651 //
652 // Set up a memory regions to access GPIO
653 //
654 void setup_io() {
655   /* open /dev/mem */
656   if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
657     printf("can't open /dev/mem \n");
658     exit(-1);
659   }
660
661   /* mmap GPIO */
662   gpio_map = mmap(
663       NULL,                    // Any adddress in our space will do
664       BCM2708_PERI_SIZE,       // Map length
665       PROT_READ | PROT_WRITE,  // Enable reading & writting to mapped memory
666       MAP_SHARED,              // Shared with other processes
667       mem_fd,                  // File to map
668       BCM2708_PERI_BASE        // Offset to GPIO peripheral
669   );
670
671   close(mem_fd);  // No need to keep mem_fd open after mmap
672
673   if (gpio_map == MAP_FAILED) {
674     printf("gpio mmap error %d\n", (int)gpio_map);  // errno also set!
675     exit(-1);
676   }
677
678   gpio = ((volatile unsigned *)gpio_map) + GPIO_ADDR / 4;
679   gpclk = ((volatile unsigned *)gpio_map) + GPCLK_ADDR / 4;
680
681 }  // setup_io