Wed, 13 Mar 2013 00:43:25 +0000
[wd2010,main] WD2010 disc geometry fixes
I believe I have fixed the geometry problem with FreeBee. The geometry was set
to 17 sectors per track instead of 16, which obviously throws off addressing.
I changed it to use 16 sectors per track. However, s4diag tries to format
sector 17, so I changed the WD2010 emulation to accept any address when
formatting (since the format command doesn't actually do anything, it doesn't
matter). It is now possible to format the hard disk, initialize the file
system, and mount it. However, cpio still fails to copy the system to the hard
disk.
Author: Andrew Warkentin <andreww591 gmail com>
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <stdbool.h>
5 #include <malloc.h>
6 #include <string.h>
8 #include "SDL.h"
10 #include "musashi/m68k.h"
11 #include "version.h"
12 #include "state.h"
13 #include "memory.h"
15 void FAIL(char *err)
16 {
17 state_done();
18 fprintf(stderr, "ERROR: %s\nExiting...\n", err);
19 exit(EXIT_FAILURE);
20 }
22 static int load_fd()
23 {
25 int writeable = 1;
26 state.fdc_disc = fopen("discim", "r+b");
27 if (!state.fdc_disc){
28 writeable = 0;
29 state.fdc_disc = fopen("discim", "rb");
30 }
31 if (!state.fdc_disc){
32 fprintf(stderr, "ERROR loading disc image 'discim'.\n");
33 state.fdc_disc = NULL;
34 return (0);
35 }else{
36 wd2797_load(&state.fdc_ctx, state.fdc_disc, 512, 10, 2, writeable);
37 fprintf(stderr, "Disc image loaded.\n");
38 return (1);
39 }
40 }
42 static int load_hd()
43 {
45 state.hdc_disc0 = fopen("hd.img", "r+b");
46 if (!state.hdc_disc0){
47 fprintf(stderr, "ERROR loading disc image 'hd.img'.\n");
48 state.hdc_disc0 = NULL;
49 return (0);
50 }else{
51 wd2010_init(&state.hdc_ctx, state.hdc_disc0, 512, 16, 8);
52 fprintf(stderr, "Disc image loaded.\n");
53 return (1);
54 }
55 }
59 /**
60 * @brief Set the pixel at (x, y) to the given value
61 * @note The surface must be locked before calling this!
62 * @param surface SDL surface upon which to draw
63 * @param x X co-ordinate
64 * @param y Y co-ordinate
65 * @param pixel Pixel value (from SDL_MapRGB)
66 */
67 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
68 {
69 int bpp = surface->format->BytesPerPixel;
70 /* Here p is the address to the pixel we want to set */
71 Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
73 switch (bpp) {
74 case 1:
75 *p = pixel;
76 break;
78 case 2:
79 *(Uint16 *)p = pixel;
80 break;
82 case 3:
83 if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
84 p[0] = (pixel >> 16) & 0xff;
85 p[1] = (pixel >> 8) & 0xff;
86 p[2] = pixel & 0xff;
87 }
88 else {
89 p[0] = pixel & 0xff;
90 p[1] = (pixel >> 8) & 0xff;
91 p[2] = (pixel >> 16) & 0xff;
92 }
93 break;
95 case 4:
96 *(Uint32 *)p = pixel;
97 break;
99 default:
100 break; /* shouldn't happen, but avoids warnings */
101 } // switch
102 }
105 /**
106 * @brief Refresh the screen.
107 * @param surface SDL surface upon which to draw.
108 */
109 void refreshScreen(SDL_Surface *s)
110 {
111 // Lock the screen surface (if necessary)
112 if (SDL_MUSTLOCK(s)) {
113 if (SDL_LockSurface(s) < 0) {
114 fprintf(stderr, "ERROR: Unable to lock screen!\n");
115 exit(EXIT_FAILURE);
116 }
117 }
119 // Map the foreground and background colours
120 Uint32 fg = SDL_MapRGB(s->format, 0x00, 0xFF, 0x00); // green foreground
121 // Uint32 fg = SDL_MapRGB(s->format, 0xFF, 0xC1, 0x06); // amber foreground
122 // Uint32 fg = SDL_MapRGB(s->format, 0xFF, 0xFF, 0xFF); // white foreground
123 Uint32 bg = SDL_MapRGB(s->format, 0x00, 0x00, 0x00); // black background
125 // Refresh the 3B1 screen area first. TODO: only do this if VRAM has actually changed!
126 uint32_t vram_address = 0;
127 for (int y=0; y<348; y++) {
128 for (int x=0; x<720; x+=16) { // 720 pixels, monochrome, packed into 16bit words
129 // Get the pixel
130 uint16_t val = RD16(state.vram, vram_address, sizeof(state.vram)-1);
131 vram_address += 2;
132 // Now copy it to the video buffer
133 for (int px=0; px<16; px++) {
134 if (val & 1)
135 putpixel(s, x+px, y, fg);
136 else
137 putpixel(s, x+px, y, bg);
138 val >>= 1;
139 }
140 }
141 }
143 // TODO: blit LEDs and status info
145 // Unlock the screen surface
146 if (SDL_MUSTLOCK(s)) {
147 SDL_UnlockSurface(s);
148 }
150 // Trigger a refresh -- TODO: partial refresh depending on whether we
151 // refreshed the screen area, status area, both, or none. Use SDL_UpdateRect() for this.
152 SDL_Flip(s);
153 }
155 /**
156 * @brief Handle events posted by SDL.
157 */
158 bool HandleSDLEvents(SDL_Surface *screen)
159 {
160 SDL_Event event;
161 while (SDL_PollEvent(&event))
162 {
163 if ((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) {
164 keyboard_event(&state.kbd, &event);
165 }
167 switch (event.type) {
168 case SDL_QUIT:
169 // Quit button tagged. Exit.
170 return true;
171 case SDL_KEYDOWN:
172 switch (event.key.keysym.sym) {
173 case SDLK_F11:
174 if (state.fdc_disc) {
175 wd2797_unload(&state.fdc_ctx);
176 fclose(state.fdc_disc);
177 state.fdc_disc = NULL;
178 fprintf(stderr, "Disc image unloaded.\n");
179 } else {
180 load_fd();
181 }
182 break;
183 case SDLK_F12:
184 if (event.key.keysym.mod & (KMOD_LALT | KMOD_RALT))
185 // ALT-F12 pressed; exit emulator
186 return true;
187 break;
188 default:
189 break;
190 }
191 break;
192 default:
193 break;
194 }
195 }
197 return false;
198 }
201 /****************************
202 * blessed be thy main()...
203 ****************************/
205 int main(void)
206 {
207 // copyright banner
208 printf("FreeBee: A Quick-and-Dirty AT&T 3B1 Emulator. Version %s, %s mode.\n", VER_FULLSTR, VER_BUILD_TYPE);
209 printf("Copyright (C) 2010 P. A. Pemberton. All rights reserved.\nLicensed under the Apache License Version 2.0.\n");
210 printf("Musashi M680x0 emulator engine developed by Karl Stenerud <kstenerud@gmail.com>\n");
211 printf("Built %s by %s@%s.\n", VER_COMPILE_DATETIME, VER_COMPILE_BY, VER_COMPILE_HOST);
212 printf("Compiler: %s\n", VER_COMPILER);
213 printf("CFLAGS: %s\n", VER_CFLAGS);
214 printf("\n");
216 // set up system state
217 // 512K of RAM
218 int i;
219 if ((i = state_init(2048*1024, 2048*1024)) != STATE_E_OK) {
220 fprintf(stderr, "ERROR: Emulator initialisation failed. Error code %d.\n", i);
221 return i;
222 }
224 // set up musashi and reset the CPU
225 m68k_set_cpu_type(M68K_CPU_TYPE_68010);
226 m68k_pulse_reset();
228 // Set up SDL
229 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
230 printf("Could not initialise SDL: %s.\n", SDL_GetError());
231 exit(EXIT_FAILURE);
232 }
234 // Make sure SDL cleans up after itself
235 atexit(SDL_Quit);
237 // Set up the video display
238 SDL_Surface *screen = NULL;
239 if ((screen = SDL_SetVideoMode(720, 384, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) {
240 printf("Could not find a suitable video mode: %s.\n", SDL_GetError());
241 exit(EXIT_FAILURE);
242 }
243 printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel);
244 SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee");
246 // Load a disc image
247 load_fd();
249 load_hd();
251 /***
252 * The 3B1 CPU runs at 10MHz, with DMA running at 1MHz and video refreshing at
253 * around 60Hz (???), with a 60Hz periodic interrupt.
254 */
255 const uint32_t SYSTEM_CLOCK = 10e6; // Hz
256 const uint32_t TIMESLOT_FREQUENCY = 1000;//240; // Hz
257 const uint32_t MILLISECS_PER_TIMESLOT = 1e3 / TIMESLOT_FREQUENCY;
258 const uint32_t CLOCKS_PER_60HZ = (SYSTEM_CLOCK / 60);
259 uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT;
260 uint32_t clock_cycles = 0, tmp;
261 bool exitEmu = false;
263 /*bool lastirq_fdc = false;*/
264 for (;;) {
265 // Run the CPU for however many cycles we need to. CPU core clock is
266 // 10MHz, and we're running at 240Hz/timeslot. Thus: 10e6/240 or
267 // 41667 cycles per timeslot.
268 tmp = m68k_execute(SYSTEM_CLOCK/TIMESLOT_FREQUENCY);
269 clock_cycles += tmp;
271 // Run the DMA engine
272 if (state.dmaen) {
273 // DMA ready to go -- so do it.
274 size_t num = 0;
275 while (state.dma_count < 0x4000) {
276 uint16_t d = 0;
277 // num tells us how many words we've copied. If this is greater than the per-timeslot DMA maximum, bail out!
278 if (num > (1e6/TIMESLOT_FREQUENCY)) break;
280 // Evidently we have more words to copy. Copy them.
281 if (state.fd_selected){
282 if (!wd2797_get_drq(&state.fdc_ctx)) {
283 // Bail out, no data available. Try again later.
284 break;
285 }
286 }else if (state.hd_selected){
287 if (!wd2010_get_drq(&state.hdc_ctx)) {
288 // Bail out, no data available. Try again later.
289 break;
290 }
291 }else{
292 printf("ERROR: DMA attempt with no drive selected!\n");
293 }
294 if (!access_check_dma(state.dma_reading)) {
295 break;
296 }
297 uint32_t newAddr;
298 // Map logical address to a physical RAM address
299 newAddr = mapAddr(state.dma_address, !state.dma_reading);
301 if (!state.dma_reading) {
302 // Data available. Get it from the FDC or HDC.
303 if (state.fd_selected) {
304 d = wd2797_read_reg(&state.fdc_ctx, WD2797_REG_DATA);
305 d <<= 8;
306 d += wd2797_read_reg(&state.fdc_ctx, WD2797_REG_DATA);
307 }else if (state.hd_selected) {
308 d = wd2010_read_data(&state.hdc_ctx);
309 d <<= 8;
310 d += wd2010_read_data(&state.hdc_ctx);
311 }
312 if (newAddr <= 0x1FFFFF) {
313 WR16(state.base_ram, newAddr, state.base_ram_size - 1, d);
314 } else if (newAddr >= 0x200000) {
315 WR16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1, d);
316 }
317 } else {
318 // Data write to FDC or HDC.
320 // Get the data from RAM
321 if (newAddr <= 0x1fffff) {
322 d = RD16(state.base_ram, newAddr, state.base_ram_size - 1);
323 } else {
324 if (newAddr <= (state.exp_ram_size + 0x200000 - 1))
325 d = RD16(state.exp_ram, newAddr - 0x200000, state.exp_ram_size - 1);
326 else
327 d = 0xffff;
328 }
330 // Send the data to the FDD or HDD
331 if (state.fd_selected){
332 wd2797_write_reg(&state.fdc_ctx, WD2797_REG_DATA, (d >> 8));
333 wd2797_write_reg(&state.fdc_ctx, WD2797_REG_DATA, (d & 0xff));
334 }else if (state.hd_selected){
335 wd2010_write_data(&state.hdc_ctx, (d >> 8));
336 wd2010_write_data(&state.hdc_ctx, (d & 0xff));
337 }
338 }
340 // Increment DMA address
341 state.dma_address+=2;
342 // Increment number of words transferred
343 num++; state.dma_count++;
344 }
346 // Turn off DMA engine if we finished this cycle
347 if (state.dma_count >= 0x4000) {
348 // FIXME? apparently this isn't required... or is it?
349 state.dma_count = 0x3fff;
350 /*state.dmaen = false;*/
351 }
352 }else if (wd2010_get_drq(&state.hdc_ctx)){
353 wd2010_dma_miss(&state.hdc_ctx);
354 }else if (wd2797_get_drq(&state.fdc_ctx)){
355 wd2797_dma_miss(&state.fdc_ctx);
356 }
359 // Any interrupts? --> TODO: masking
360 /* if (!lastirq_fdc) {
361 if (wd2797_get_irq(&state.fdc_ctx)) {
362 lastirq_fdc = true;
363 m68k_set_irq(2);
364 }
365 }
366 */
367 if (wd2797_get_irq(&state.fdc_ctx) || wd2010_get_irq(&state.hdc_ctx)) {
368 m68k_set_irq(2);
369 }else if (keyboard_get_irq(&state.kbd)) {
370 m68k_set_irq(3);
371 } else {
372 // if (!state.timer_asserted){
373 m68k_set_irq(0);
374 // }
375 }
377 // Is it time to run the 60Hz periodic interrupt yet?
378 if (clock_cycles > CLOCKS_PER_60HZ) {
379 // Refresh the screen
380 refreshScreen(screen);
381 if (state.timer_enabled){
382 m68k_set_irq(6);
383 state.timer_asserted = true;
384 }
385 // scan the keyboard
386 keyboard_scan(&state.kbd);
387 // decrement clock cycle counter, we've handled the intr.
388 clock_cycles -= CLOCKS_PER_60HZ;
389 }
391 // handle SDL events -- returns true if we need to exit
392 if (HandleSDLEvents(screen))
393 exitEmu = true;
395 // make sure frame rate is equal to real time
396 uint32_t now = SDL_GetTicks();
397 if (now < next_timeslot) {
398 // timeslot finished early -- eat up some time
399 SDL_Delay(next_timeslot - now);
400 } else {
401 // timeslot finished late -- skip ahead to gain time
402 // TODO: if this happens a lot, we should let the user know
403 // that their PC might not be fast enough...
404 next_timeslot = now;
405 }
406 // advance to the next timeslot
407 next_timeslot += MILLISECS_PER_TIMESLOT;
409 // if we've been asked to exit the emulator, then do so.
410 if (exitEmu) break;
411 }
413 // Release the disc image
414 wd2797_unload(&state.fdc_ctx);
415 fclose(state.fdc_disc);
417 return 0;
418 }