main
nor.c
1#include "genesis.h"
2#include <stdlib.h>
3#include <string.h>
4#include "util.h"
5
6enum {
7 NOR_NORMAL,
8 NOR_PRODUCTID,
9 NOR_BOOTBLOCK
10};
11
12enum {
13 NOR_CMD_IDLE,
14 NOR_CMD_AA,
15 NOR_CMD_55
16};
17
18//Technically this value shoudl be slightly different between NTSC and PAL
19//as it's defined as 200 micro-seconds, not in clock cycles
20#define NOR_WRITE_PAUSE 10690
21
22void nor_flash_init(nor_state *state, uint8_t *buffer, uint32_t size, uint32_t page_size, uint16_t product_id, uint8_t bus_flags)
23{
24 state->buffer = buffer;
25 state->page_buffer = malloc(page_size);
26 memset(state->page_buffer, 0xFF, page_size);
27 state->size = size;
28 state->page_size = page_size;
29 state->product_id = product_id;
30 state->last_write_cycle = 0xFFFFFFFF;
31 state->mode = NOR_NORMAL;
32 state->cmd_state = NOR_CMD_IDLE;
33 state->alt_cmd = 0;
34 state->bus_flags = bus_flags;
35 state->cmd_address1 = 0x5555;
36 state->cmd_address2 = 0x2AAA;
37}
38
39void nor_run(nor_state *state, m68k_context *m68k, uint32_t cycle)
40{
41 if (state->last_write_cycle == 0xFFFFFFFF) {
42 return;
43 }
44 if (cycle - state->last_write_cycle >= NOR_WRITE_PAUSE) {
45 state->last_write_cycle = 0xFFFFFFFF;
46 for (uint32_t i = 0; i < state->page_size; i++) {
47 state->buffer[state->current_page + i] = state->page_buffer[i];
48 }
49 memset(state->page_buffer, 0xFF, state->page_size);
50 if (state->bus_flags == RAM_FLAG_BOTH) {
51 //TODO: add base address of NOR device to start and end addresses
52 m68k_invalidate_code_range(m68k, state->current_page, state->current_page + state->page_size);
53 }
54 }
55}
56
57uint8_t nor_flash_read_b(uint32_t address, void *vcontext)
58{
59 m68k_context *m68k = vcontext;
60 genesis_context *gen = m68k->system;
61 nor_state *state = &gen->nor;
62 if (
63 ((address & 1) && state->bus_flags == RAM_FLAG_EVEN) ||
64 (!(address & 1) && state->bus_flags == RAM_FLAG_ODD)
65 ) {
66 return 0xFF;
67 }
68 if (state->bus_flags != RAM_FLAG_BOTH) {
69 address = address >> 1;
70 }
71
72 nor_run(state, m68k, m68k->current_cycle);
73 switch (state->mode)
74 {
75 case NOR_NORMAL:
76 if (state->bus_flags == RAM_FLAG_BOTH) {
77 address ^= 1;
78 }
79 return state->buffer[address & (state->size-1)];
80 break;
81 case NOR_PRODUCTID:
82 switch (address & (state->size - 1))
83 {
84 case 0:
85 return state->product_id >> 8;
86 case 1:
87 return state->product_id;
88 case 2:
89 //TODO: Implement boot block protection
90 return 0xFE;
91 default:
92 return 0xFE;
93 } //HERE
94 break;
95 case NOR_BOOTBLOCK:
96 break;
97 }
98 return 0xFF;
99}
100
101uint16_t nor_flash_read_w(uint32_t address, void *context)
102{
103 uint16_t value = nor_flash_read_b(address, context) << 8;
104 value |= nor_flash_read_b(address+1, context);
105 return value;
106}
107
108void nor_write_byte(nor_state *state, uint32_t address, uint8_t value, uint32_t cycle)
109{
110 switch(state->mode)
111 {
112 case NOR_NORMAL:
113 if (state->last_write_cycle != 0xFFFFFFFF) {
114 state->current_page = address & (state->size - 1) & ~(state->page_size - 1);
115 }
116 if (state->bus_flags == RAM_FLAG_BOTH) {
117 address ^= 1;
118 }
119 state->page_buffer[address & (state->page_size - 1)] = value;
120 break;
121 case NOR_PRODUCTID:
122 break;
123 case NOR_BOOTBLOCK:
124 //TODO: Implement boot block protection
125 state->mode = NOR_NORMAL;
126 break;
127 }
128}
129
130void *nor_flash_write_b(uint32_t address, void *vcontext, uint8_t value)
131{
132 m68k_context *m68k = vcontext;
133 genesis_context *gen = m68k->system;
134 nor_state *state = &gen->nor;
135 if (
136 ((address & 1) && state->bus_flags == RAM_FLAG_EVEN) ||
137 (!(address & 1) && state->bus_flags == RAM_FLAG_ODD)
138 ) {
139 return vcontext;
140 }
141 if (state->bus_flags != RAM_FLAG_BOTH) {
142 address = address >> 1;
143 }
144
145 nor_run(state, m68k, m68k->current_cycle);
146 switch (state->cmd_state)
147 {
148 case NOR_CMD_IDLE:
149 if (value == 0xAA && (address & (state->size - 1)) == state->cmd_address1) {
150 state->cmd_state = NOR_CMD_AA;
151 } else {
152 nor_write_byte(state, address, value, m68k->current_cycle);
153 state->cmd_state = NOR_CMD_IDLE;
154 }
155 break;
156 case NOR_CMD_AA:
157 if (value == 0x55 && (address & (state->size - 1)) == state->cmd_address2) {
158 state->cmd_state = NOR_CMD_55;
159 } else {
160 nor_write_byte(state, state->cmd_address1, 0xAA, m68k->current_cycle);
161 nor_write_byte(state, address, value, m68k->current_cycle);
162 state->cmd_state = NOR_CMD_IDLE;
163 }
164 break;
165 case NOR_CMD_55:
166 if ((address & (state->size - 1)) == state->cmd_address1) {
167 if (state->alt_cmd) {
168 switch(value)
169 {
170 case 0x10:
171 puts("UNIMPLEMENTED: NOR flash erase");
172 break;
173 case 0x20:
174 puts("UNIMPLEMENTED: NOR flash disable protection");
175 break;
176 case 0x40:
177 state->mode = NOR_BOOTBLOCK;
178 break;
179 case 0x60:
180 state->mode = NOR_PRODUCTID;
181 break;
182 }
183 } else {
184 switch(value)
185 {
186 case 0x80:
187 state->alt_cmd = 1;
188 break;
189 case 0x90:
190 state->mode = NOR_PRODUCTID;
191 break;
192 case 0xA0:
193 puts("UNIMPLEMENTED: NOR flash enable protection");
194 break;
195 case 0xF0:
196 state->mode = NOR_NORMAL;
197 break;
198 default:
199 printf("Unrecognized unshifted NOR flash command %X\n", value);
200 }
201 }
202 } else {
203 nor_write_byte(state, state->cmd_address1, 0xAA, m68k->current_cycle);
204 nor_write_byte(state, state->cmd_address2, 0x55, m68k->current_cycle);
205 nor_write_byte(state, address, value, m68k->current_cycle);
206 }
207 state->cmd_state = NOR_CMD_IDLE;
208 break;
209 }
210 return vcontext;
211}
212
213void *nor_flash_write_w(uint32_t address, void *vcontext, uint16_t value)
214{
215 nor_flash_write_b(address, vcontext, value >> 8);
216 return nor_flash_write_b(address + 1, vcontext, value);
217}