Created a support folder and move core ARM support code for minimig into it. Updated menu, osd, and user_io. Makefile also updated to account for new support folder

This commit is contained in:
karllurman
2018-10-31 23:11:07 +11:00
parent 7614950f67
commit 94e560fa58
14 changed files with 467 additions and 462 deletions

View File

@@ -0,0 +1,476 @@
// boot.c
// bootscreen functions
// 2014, rok.krajnc@gmail.com
#include "string.h"
#include "stdio.h"
#include "minimig_boot.h"
#include "../../hardware.h"
#include "../../osd.h"
#include "../../spi.h"
#include "../../file_io.h"
#include "minimig_config.h"
#include "minimig_fdd.h"
#include "../../cfg.h"
static uint8_t buffer[1024];
static void mem_upload_init(unsigned long addr)
{
spi_osd_cmd32le_cont(OSD_CMD_WR, addr);
}
static void mem_upload_fini()
{
DisableOsd();
}
static void mem_write16(unsigned short x)
{
spi8((((x) >> 8) & 0xff)); spi8(((x)& 0xff));
}
//// boot cursor positions ////
unsigned short bcurx = 0;
unsigned short bcury = 96;
static int bootscreen_adr = 0x80000 + /*120*/112 * 640 / 8;
void BootHome()
{
bootscreen_adr = 0x80000 + /*120*/112 * 640 / 8;
}
static const char boot_font[96][8] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // SPACE
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00 }, // !
{ 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // "
{ 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00 }, // #
{ 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00 }, // $
{ 0x00, 0x66, 0xAC, 0xD8, 0x36, 0x6A, 0xCC, 0x00 }, // %
{ 0x38, 0x6C, 0x68, 0x76, 0xDC, 0xCE, 0x7B, 0x00 }, // &
{ 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '
{ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00 }, // (
{ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00 }, // )
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, // *
{ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00 }, // +
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30 }, // ,
{ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00 }, // -
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00 }, // .
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00 }, // /
{ 0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00 }, // 0
{ 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x00 }, // 1
{ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00 }, // 2
{ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00 }, // 3
{ 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x00 }, // 4
{ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00 }, // 5
{ 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00 }, // 6
{ 0x7E, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x00 }, // 7
{ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00 }, // 8
{ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00 }, // 9
{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00 }, // :
{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30 }, // ;
{ 0x00, 0x06, 0x18, 0x60, 0x18, 0x06, 0x00, 0x00 }, // <
{ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00 }, // =
{ 0x00, 0x60, 0x18, 0x06, 0x18, 0x60, 0x00, 0x00 }, // >
{ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00 }, // ?
{ 0x7C, 0xC6, 0xDE, 0xD6, 0xDE, 0xC0, 0x78, 0x00 }, // @
{ 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, // A
{ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00 }, // B
{ 0x1E, 0x30, 0x60, 0x60, 0x60, 0x30, 0x1E, 0x00 }, // C
{ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00 }, // D
{ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00 }, // E
{ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00 }, // F
{ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3E, 0x00 }, // G
{ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, // H
{ 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00 }, // I
{ 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00 }, // J
{ 0xC6, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0xC6, 0x00 }, // K
{ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00 }, // L
{ 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00 }, // M
{ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00 }, // N
{ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // O
{ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00 }, // P
{ 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x7E, 0x00 }, // Q
{ 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00 }, // R
{ 0x3C, 0x66, 0x70, 0x3C, 0x0E, 0x66, 0x3C, 0x00 }, // S
{ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, // T
{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // U
{ 0x66, 0x66, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x00 }, // V
{ 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00 }, // W
{ 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00 }, // X
{ 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00 }, // Y
{ 0xFE, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFE, 0x00 }, // Z
{ 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00 }, // [
{ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00 }, // Backslash
{ 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00 }, // ]
{ 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00 }, // ^
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE }, // _
{ 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }, // `
{ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00 }, // a
{ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00 }, // b
{ 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00 }, // c
{ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00 }, // d
{ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00 }, // e
{ 0x1C, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x30, 0x00 }, // f
{ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C }, // g
{ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00 }, // h
{ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x0C, 0x00 }, // i
{ 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x78 }, // j
{ 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00 }, // k
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0C, 0x00 }, // l
{ 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xC6, 0xC6, 0x00 }, // m
{ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00 }, // n
{ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // o
{ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60 }, // p
{ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06 }, // q
{ 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00 }, // r
{ 0x00, 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x7C, 0x00 }, // s
{ 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00 }, // t
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00 }, // u
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00 }, // v
{ 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00 }, // w
{ 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00 }, // x
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x30 }, // y
{ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00 }, // z
{ 0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00 }, // {
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, // |
{ 0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00 }, // }
{ 0x72, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ~
{ 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00 } //
};
static void BootEnableMem()
{
// TEMP enable 1MB memory
spi_osd_cmd8(OSD_CMD_MEM, 0x5);
//EnableOsd();
//spi8(OSD_CMD_RST);
//rstval = (SPI_CPU_HLT | SPI_RST_CPU);
//spi8(rstval);
//DisableOsd();
//SPIN(); SPIN(); SPIN(); SPIN();
//while ((read32(REG_SYS_STAT_ADR) & 0x2));
}
static void BootClearScreen(int adr, int size)
{
int i;
mem_upload_init(adr);
for (i = 0; i<size; i++)
{
mem_write16(0x0000);
//mem_write16(i);
}
mem_upload_fini();
}
static void BootUploadLogo()
{
fileTYPE file = { 0 };
int x, y;
int i = 0;
int adr;
if (FileOpen(&file, "Amiga/" LOGO_FILE) || FileOpen(&file, LOGO_FILE)) {
FileReadSec(&file, buffer);
mem_upload_init(SCREEN_BPL1 + LOGO_OFFSET);
adr = SCREEN_BPL1 + LOGO_OFFSET;
for (y = 0; y<LOGO_HEIGHT; y++)
{
for (x = 0; x<LOGO_WIDTH / 16; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
//for (tmp=0; tmp<0x80000; tmp++);
//printf("i=%03d x=%03d y=%03d dat[0]=0x%08x dat[1]=0x%08x\n", i, x, y, buffer[i], buffer[i+1]);
adr += 2;
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL1 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8));
adr = SCREEN_BPL1 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8);
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL2 + LOGO_OFFSET);
adr = SCREEN_BPL2 + LOGO_OFFSET;
for (y = 0; y<LOGO_HEIGHT; y++)
{
for (x = 0; x<LOGO_WIDTH / 16; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL2 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8));
adr = SCREEN_BPL2 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8);
}
mem_upload_fini();
FileClose(&file);
}
}
static void BootUploadBall()
{
fileTYPE file = { 0 };
int x;
int i = 0;
int adr;
if (FileOpen(&file, "Amiga/" BALL_FILE) || FileOpen(&file, BALL_FILE))
{
FileReadSec(&file, buffer);
mem_upload_init(BALL_ADDRESS);
adr = BALL_ADDRESS;
for (x = 0; x<BALL_SIZE / 2; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
FileClose(&file);
}
}
static void BootUploadCopper()
{
fileTYPE file = { 0 };
int x;
int i = 0;
int adr;
if (FileOpen(&file, "Amiga/" COPPER_FILE) || FileOpen(&file, COPPER_FILE))
{
FileReadSec(&file, buffer);
mem_upload_init(COPPER_ADDRESS);
adr = COPPER_ADDRESS;
for (x = 0; x<COPPER_SIZE / 2; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
FileClose(&file);
}
else {
mem_upload_init(COPPER_ADDRESS);
mem_write16(0x00e0); mem_write16(0x0008);
mem_write16(0x00e2); mem_write16(0x0000);
mem_write16(0x00e4); mem_write16(0x0008);
mem_write16(0x00e6); mem_write16(0x5000);
mem_write16(0x0100); mem_write16(0xa200);
mem_write16(0xffff); mem_write16(0xfffe);
mem_upload_fini();
}
}
static void BootCustomInit()
{
//move.w #$0000,$dff1fc ; FMODE, slow fetch mode for AGA compatibility
mem_upload_init(0xdff1fc);
mem_write16(0x0000);
mem_upload_fini();
//move.w #$0002,$dff02e ; COPCON, enable danger mode
mem_upload_init(0xdff02e);
mem_write16(0x0002);
mem_upload_fini();
//move.l #Copper1,$dff080 ; COP1LCH, copper 1 pointer
//move.l #Copper2,$dff084 ; CPO2LCH, copper 2 pointer
mem_upload_init(0xdff080);
mem_write16(0x0008); mem_write16(0xe680);
mem_write16(0x0008); mem_write16(0xe69c);
mem_upload_fini();
//move.w #$2c81,$dff08e ; DIWSTRT, screen upper left corner
//move.w #$f4c1,$dff090 ; DIWSTOP, screen lower right corner
//move.w #$003c,$dff092 ; DDFSTRT, display data fetch start
//move.w #$00d4,$dff094 ; DDFSTOP, display data fetch stop
//move.w #$87c0,$dff096 ; DMACON, enable important bits
//move.w #$0000,$dff098 ; CLXCON, TODO
//move.w #$7fff,$dff09a ; INTENA, disable all interrupts
//move.w #$7fff,$dff09c ; INTREQ, disable all interrupts
//move.w #$0000,$dff09e ; ADKCON, TODO
mem_upload_init(0xdff08e);
//mem_write16(0x1d64);
//mem_write16(0x38c7);
//mem_write16(0x0028);
//mem_write16(0x00d8);
mem_write16(0x2c81);
mem_write16(0xf4c1);
mem_write16(0x003c);
mem_write16(0x00d4);
mem_write16(0x87c0);
mem_write16(0x0000);
mem_write16(0x7fff);
mem_write16(0x7fff);
mem_upload_fini();
//move.w #(bpl1>>16)&$ffff,$dff0e0 ; BPL1PTH
//move.w #bpl1&$ffff,$dff0e2 ; BPL1PTL
//move.w #(bpl2>>16)&$ffff,$dff0e4 ; BPL2PTH
//move.w #bpl2&$ffff,$dff0e6 ; BPL2PTL
mem_upload_init(0xdff0e0);
mem_write16(0x0008); mem_write16(0x0000);
mem_write16(0x0008); mem_write16(0x5000);
mem_upload_fini();
//move.w #$a200,$dff100 ; BPLCON0, two bitplanes & colorburst enabled
//move.w #$0000,$dff102 ; BPLCON1, bitplane control scroll value
//move.w #$0000,$dff104 ; BPLCON2, misc bitplane bits
//move.w #$0000,$dff106 ; BPLCON3, TODO
//move.w #$0000,$dff108 ; BPL1MOD, bitplane modulo for odd planes
//move.w #$0000,$dff10a ; BPL2MOD, bitplane modulo for even planes
mem_upload_init(0xdff100);
mem_write16(0xa200);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_upload_fini();
//move.w #$09f0,$dff040 ; BLTCON0
//move.w #$0000,$dff042 ; BLTCON1
//move.w #$ffff,$dff044 ; BLTAFWM, blitter first word mask for srcA
//move.w #$ffff,$dff046 ; BLTALWM, blitter last word mask for srcA
mem_upload_init(0xdff040);
mem_write16(0x09f0);
mem_write16(0x0000);
mem_write16(0xffff);
mem_write16(0xffff);
mem_upload_fini();
//move.w #$0000,$dff064 ; BLTAMOD
//move.w #BLITS,$dff066 ; BLTDMOD
mem_upload_init(0xdff064);
mem_write16(0x0000);
mem_write16(BLITS);
mem_upload_fini();
//move.w #$0000,$dff180 ; COLOR00
//move.w #$0aaa,$dff182 ; COLOR01
//move.w #$0a00,$dff184 ; COLOR02
//move.w #$0000,$dff186 ; COLOR03
mem_upload_init(0xdff180);
mem_write16(0x0000);
mem_write16(0x0aaa);
mem_write16(0x0a00);
mem_write16(0x000a);
mem_upload_fini();
//move.w #$0000,$dff088 ; COPJMP1, restart copper at location 1
mem_upload_init(0xdff088);
mem_write16(0x0000);
mem_upload_fini();
}
void BootInit()
{
puts("Running minimig setup");
EnableOsd();
spi8(OSD_CMD_VERSION);
char ver_beta = spi_b(0xff);
char ver_major = spi_b(0xff);
char ver_minor = spi_b(0xff);
char ver_minion = spi_b(0xff);
DisableOsd();
spi8(OSD_CMD_RST);
rstval = (SPI_RST_USR | SPI_RST_CPU | SPI_CPU_HLT);
spi8(rstval);
DisableOsd();
EnableOsd();
spi8(OSD_CMD_RST);
rstval = (SPI_RST_CPU | SPI_CPU_HLT);
spi8(rstval);
DisableOsd();
if (cfg.bootscreen)
{
//default video config till real config loaded.
ConfigVideo(0, 0, 0x40);
ConfigAudio(0);
WaitTimer(100);
BootEnableMem();
BootClearScreen(SCREEN_ADDRESS, SCREEN_MEM_SIZE);
BootUploadLogo();
BootUploadBall();
BootUploadCopper();
BootCustomInit();
WaitTimer(500);
char rtl_ver[128];
sprintf(rtl_ver, "MINIMIG-AGA%s v%d.%d.%d by Rok Krajnc. MiSTer port by Sorgelig.", ver_beta ? " BETA" : "", ver_major, ver_minor, ver_minion);
BootPrintEx(rtl_ver);
BootPrintEx(" ");
BootPrintEx("Original Minimig by Dennis van Weeren");
BootPrintEx("Updates by Jakub Bednarski, Tobias Gubener, Sascha Boing, A.M. Robinson & others");
BootPrintEx(" ");
}
config.kickstart[0] = 0;
LoadConfiguration(0);
}
void BootPrintEx(const char * str)
{
unsigned char i, j;
unsigned char len;
printf(str);
printf("\n");
len = strlen(str);
len = (len>80) ? 80 : len;
for (j = 0; j<8; j++)
{
mem_upload_init(bootscreen_adr);
for (i = 0; i<len; i += 2)
{
spi8(boot_font[str[i] - 32][j]);
if (i == (len - 1))
spi8(boot_font[0][j]);
else
spi8(boot_font[str[i + 1] - 32][j]);
}
mem_upload_fini();
bootscreen_adr += 640 / 8;
}
}

View File

@@ -0,0 +1,45 @@
// boot.h
// bootscreen functions
// 2014, rok.krajnc@gmail.com
#ifndef __MINIMIG_BOOT_H__
#define __MINIMIG_BOOT_H__
//// defines ////
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 256
#define SCREEN_SIZE SCREEN_WIDTH * SCREEN_HEIGHT
#define SCREEN_MEM_SIZE 2*SCREEN_SIZE/8
#define SCREEN_ADDRESS 0x80000
#define SCREEN_BPL1 0x80000
#define SCREEN_BPL2 0x85000
#define LOGO_WIDTH 208
#define LOGO_HEIGHT 32
#define LOGO_OFFSET (64*SCREEN_WIDTH/8+24)
#define LOGO_LSKIP (SCREEN_WIDTH-LOGO_WIDTH)/8
#define LOGO_SIZE 0x680
#define LOGO_FILE "MINIMIG.ART"
#define BALL_SIZE 0x4000
#define BALL_ADDRESS 0x8a000
#define BALL_FILE "MINIMIG.BAL"
#define COPPER_SIZE 0x35c
#define COPPER_ADDRESS 0x8e680
#define COPPER_FILE "MINIMIG.COP"
#define BLITS 64
//// functions ////
void BootInit();
void BootPrintEx(const char * str);
void BootHome();
#define BootPrint(text) printf("%s\n", text)
#endif // __BOOT_H__

View File

@@ -0,0 +1,510 @@
// config.c
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <dirent.h>
#include "../../hardware.h"
#include "minimig_boot.h"
#include "../../file_io.h"
#include "../../osd.h"
#include "minimig_fdd.h"
#include "minimig_hdd.h"
#include "../../menu.h"
#include "minimig_config.h"
#include "../../user_io.h"
#include "../../input.h"
typedef struct
{
char id[8];
unsigned long version;
char kickstart[1024];
filterTYPE filter;
unsigned char memory;
unsigned char chipset;
floppyTYPE floppy;
unsigned char disable_ar3;
unsigned char enable_ide;
unsigned char scanlines;
unsigned char audio;
hardfileTYPE hardfile[2];
unsigned char cpu;
unsigned char autofire;
} configTYPE_old;
configTYPE config = { 0 };
unsigned char romkey[3072];
static void SendFileV2(fileTYPE* file, unsigned char* key, int keysize, int address, int size)
{
static uint8_t buf[512];
unsigned int keyidx = 0;
printf("File size: %dkB\n", size >> 1);
printf("[");
if (keysize)
{
// read header
FileReadAdv(file, buf, 0xb);
}
for (int i = 0; i<size; i++)
{
if (!(i & 31)) printf("*");
FileReadAdv(file, buf, 512);
if (keysize)
{
// decrypt ROM
for (int j = 0; j<512; j++)
{
buf[j] ^= key[keyidx++];
if (keyidx >= keysize) keyidx -= keysize;
}
}
EnableOsd();
unsigned int adr = address + i * 512;
spi8(OSD_CMD_WR);
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
for (int j = 0; j<512; j = j + 4)
{
spi8(buf[j + 0]);
spi8(buf[j + 1]);
spi8(buf[j + 2]);
spi8(buf[j + 3]);
}
DisableOsd();
}
printf("]\n");
}
static char UploadKickstart(char *name)
{
fileTYPE file = { 0 };
int keysize = 0;
BootPrint("Checking for Amiga Forever key file:");
if (FileOpen(&file, "Amiga/ROM.KEY") || FileOpen(&file, "ROM.KEY")) {
keysize = file.size;
if (file.size<sizeof(romkey))
{
FileReadAdv(&file, romkey, keysize);
BootPrint("Loaded Amiga Forever key file");
}
else
{
BootPrint("Amiga Forever keyfile is too large!");
}
FileClose(&file);
}
BootPrint("Loading file: ");
BootPrint(name);
if (FileOpen(&file, name)) {
if (file.size == 0x100000) {
// 1MB Kickstart ROM
BootPrint("Uploading 1MB Kickstart ...");
SendFileV2(&file, NULL, 0, 0xe00000, file.size >> 10);
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 10);
FileClose(&file);
return(1);
}
else if (file.size == 0x80000) {
// 512KB Kickstart ROM
BootPrint("Uploading 512KB Kickstart ...");
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name);
SendFileV2(&file, NULL, 0, 0xe00000, file.size >> 9);
FileClose(&file);
return(1);
}
else if ((file.size == 0x8000b) && keysize) {
// 512KB Kickstart ROM
BootPrint("Uploading 512 KB Kickstart (Probably Amiga Forever encrypted...)");
SendFileV2(&file, romkey, keysize, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name);
SendFileV2(&file, romkey, keysize, 0xe00000, file.size >> 9);
FileClose(&file);
return(1);
}
else if (file.size == 0x40000) {
// 256KB Kickstart ROM
BootPrint("Uploading 256 KB Kickstart...");
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name); // TODO will this work
SendFileV2(&file, NULL, 0, 0xfc0000, file.size >> 9);
FileClose(&file);
return(1);
}
else if ((file.size == 0x4000b) && keysize) {
// 256KB Kickstart ROM
BootPrint("Uploading 256 KB Kickstart (Probably Amiga Forever encrypted...");
SendFileV2(&file, romkey, keysize, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name); // TODO will this work
SendFileV2(&file, romkey, keysize, 0xfc0000, file.size >> 9);
FileClose(&file);
return(1);
}
else {
BootPrint("Unsupported ROM file size!");
}
FileClose(&file);
}
else {
printf("No \"%s\" file!\n", name);
}
return(0);
}
static char UploadActionReplay()
{
fileTYPE file = { 0 };
if(FileOpen(&file, "Amiga/HRTMON.ROM") || FileOpen(&file, "HRTMON.ROM"))
{
int adr, data;
puts("Uploading HRTmon ROM... ");
SendFileV2(&file, NULL, 0, 0xa10000, (file.size + 511) >> 9);
// HRTmon config
adr = 0xa10000 + 20;
spi_osd_cmd32le_cont(OSD_CMD_WR, adr);
data = 0x00800000; // mon_size, 4 bytes
spi8((data >> 24) & 0xff); spi8((data >> 16) & 0xff);
spi8((data >> 8) & 0xff); spi8((data >> 0) & 0xff);
data = 0x00; // col0h, 1 byte
spi8((data >> 0) & 0xff);
data = 0x5a; // col0l, 1 byte
spi8((data >> 0) & 0xff);
data = 0x0f; // col1h, 1 byte
spi8((data >> 0) & 0xff);
data = 0xff; // col1l, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // right, 1 byte
spi8((data >> 0) & 0xff);
data = 0x00; // keyboard, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // key, 1 byte
spi8((data >> 0) & 0xff);
data = config.enable_ide ? 1 : 0; // ide, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // a1200, 1 byte
spi8((data >> 0) & 0xff);
data = config.chipset&CONFIG_AGA ? 1 : 0; // aga, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // insert, 1 byte
spi8((data >> 0) & 0xff);
data = 0x0f; // delay, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // lview, 1 byte
spi8((data >> 0) & 0xff);
data = 0x00; // cd32, 1 byte
spi8((data >> 0) & 0xff);
data = config.chipset&CONFIG_NTSC ? 1 : 0; // screenmode, 1 byte
spi8((data >> 0) & 0xff);
data = 1; // novbr, 1 byte
spi8((data >> 0) & 0xff);
data = 0; // entered, 1 byte
spi8((data >> 0) & 0xff);
data = 1; // hexmode, 1 byte
spi8((data >> 0) & 0xff);
DisableOsd();
adr = 0xa10000 + 68;
spi_osd_cmd32le_cont(OSD_CMD_WR, adr);
data = ((config.memory & 0x3) + 1) * 512 * 1024; // maxchip, 4 bytes TODO is this correct?
spi8((data >> 24) & 0xff); spi8((data >> 16) & 0xff);
spi8((data >> 8) & 0xff); spi8((data >> 0) & 0xff);
DisableOsd();
FileClose(&file);
return(1);
}
else {
puts("\nhrtmon.rom not found!\n");
return(0);
}
return(0);
}
static char* GetConfigurationName(int num)
{
static char path[128];
sprintf(path, "%s/%s", getRootDir(), CONFIG_DIR);
DIR *d;
struct dirent *dir;
d = opendir(path);
if (d)
{
if(num) sprintf(path, "minimig%d", num);
else sprintf(path, "minimig.cfg", num);
while ((dir = readdir(d)) != NULL)
{
int len = strlen(dir->d_name);
if (len>10 && !strncasecmp(dir->d_name, path, strlen(path)) && !strcasecmp(dir->d_name+len-4, ".cfg"))
{
closedir(d);
strcpy(path, dir->d_name);
return path;
}
}
closedir(d);
}
return NULL;
}
unsigned char SaveConfiguration(int num)
{
const char *filename = GetConfigurationName(num);
if (!filename)
{
static char name[32];
if (num) sprintf(name, "minimig%d.cfg", num);
else sprintf(name, "minimig.cfg");
filename = name;
}
return FileSaveConfig(filename, &config, sizeof(config));
}
const char* GetConfigDisplayName(int num)
{
char *filename = GetConfigurationName(num);
if (!filename) return NULL;
filename[strlen(filename) - 4] = 0;
char *p = strchr(filename, '_');
if (p) return p+1;
return "";
}
static int force_reload_kickstart = 0;
static void ApplyConfiguration(char reloadkickstart)
{
if (force_reload_kickstart) reloadkickstart = 1;
force_reload_kickstart = 0;
ConfigCPU(config.cpu);
if (!reloadkickstart)
{
ConfigChipset(config.chipset);
ConfigFloppy(config.floppy.drives, config.floppy.speed);
}
printf("CPU clock : %s\n", config.chipset & 0x01 ? "turbo" : "normal");
printf("Chip RAM size : %s\n", config_memory_chip_msg[config.memory & 0x03]);
printf("Slow RAM size : %s\n", config_memory_slow_msg[config.memory >> 2 & 0x03]);
printf("Fast RAM size : %s\n", config_memory_fast_msg[config.memory >> 4 & 0x03]);
printf("Floppy drives : %u\n", config.floppy.drives + 1);
printf("Floppy speed : %s\n", config.floppy.speed ? "fast" : "normal");
printf("\n");
printf("\nIDE state: %s.\n", config.enable_ide ? "enabled" : "disabled");
if (config.enable_ide)
{
printf("Primary Master HDD is %s.\n", config.hardfile[0].enabled ? "enabled" : "disabled");
printf("Primary Slave HDD is %s.\n", config.hardfile[1].enabled ? "enabled" : "disabled");
printf("Secondary Master HDD is %s.\n", config.hardfile[2].enabled ? "enabled" : "disabled");
printf("Secondary Slave HDD is %s.\n", config.hardfile[3].enabled ? "enabled" : "disabled");
}
rstval = SPI_CPU_HLT;
spi_osd_cmd8(OSD_CMD_RST, rstval);
spi_osd_cmd8(OSD_CMD_HDD, (config.enable_ide ? 1 : 0) | (OpenHardfile(0) ? 2 : 0) | (OpenHardfile(1) ? 4 : 0) | (OpenHardfile(2) ? 8 : 0) | (OpenHardfile(3) ? 16 : 0));
ConfigMemory(config.memory);
ConfigCPU(config.cpu);
ConfigChipset(config.chipset);
ConfigFloppy(config.floppy.drives, config.floppy.speed);
if (config.memory & 0x40) UploadActionReplay();
if (reloadkickstart)
{
printf("Reloading kickstart ...\n");
rstval |= (SPI_RST_CPU | SPI_CPU_HLT);
spi_osd_cmd8(OSD_CMD_RST, rstval);
if (!UploadKickstart(config.kickstart))
{
strcpy(config.kickstart, "Amiga/KICK.ROM");
if (!UploadKickstart(config.kickstart))
{
strcpy(config.kickstart, "KICK.ROM");
if (!UploadKickstart(config.kickstart))
{
BootPrintEx("No Kickstart loaded. Press F12 for settings.");
BootPrintEx("** Halted! **");
return;
}
}
}
rstval |= (SPI_RST_USR | SPI_RST_CPU);
spi_osd_cmd8(OSD_CMD_RST, rstval);
}
else
{
printf("Resetting ...\n");
rstval |= (SPI_RST_USR | SPI_RST_CPU);
spi_osd_cmd8(OSD_CMD_RST, rstval);
}
rstval = 0;
spi_osd_cmd8(OSD_CMD_RST, rstval);
ConfigVideo(config.filter.hires, config.filter.lores, config.scanlines);
ConfigAudio(config.audio);
ConfigAutofire(config.autofire, 0xC);
}
unsigned char LoadConfiguration(int num)
{
static const char config_id[] = "MNMGCFG0";
char updatekickstart = 0;
char result = 0;
unsigned char key, i;
const char *filename = GetConfigurationName(num);
// load configuration data
int size;
if(filename && (size = FileLoadConfig(filename, 0, 0))>0)
{
BootPrint("Opened configuration file\n");
printf("Configuration file size: %s, %lu\n", filename, size);
if (size == sizeof(config))
{
static configTYPE tmpconf;
if (FileLoadConfig(filename, &tmpconf, sizeof(tmpconf)))
{
// check file id and version
if (strncmp(tmpconf.id, config_id, sizeof(config.id)) == 0) {
// A few more sanity checks...
if (tmpconf.floppy.drives <= 4) {
// If either the old config and new config have a different kickstart file,
// or this is the first boot, we need to upload a kickstart image.
if (strcmp(tmpconf.kickstart, config.kickstart) != 0) {
updatekickstart = true;
}
memcpy((void*)&config, (void*)&tmpconf, sizeof(config));
result = 1; // We successfully loaded the config.
}
else BootPrint("Config file sanity check failed!\n");
}
else BootPrint("Wrong configuration file format!\n");
}
else printf("Cannot load configuration file\n");
}
else if (size == sizeof(configTYPE_old))
{
static configTYPE_old tmpconf;
printf("Old Configuration file.\n");
if (FileLoadConfig(filename, &tmpconf, sizeof(tmpconf)))
{
// check file id and version
if (strncmp(tmpconf.id, config_id, sizeof(config.id)) == 0) {
// A few more sanity checks...
if (tmpconf.floppy.drives <= 4) {
// If either the old config and new config have a different kickstart file,
// or this is the first boot, we need to upload a kickstart image.
if (strcmp(tmpconf.kickstart, config.kickstart) != 0) {
updatekickstart = true;
}
memcpy((void*)&config, (void*)&tmpconf, sizeof(config));
config.cpu = tmpconf.cpu;
config.autofire = tmpconf.autofire;
memset(&config.hardfile[2], 0, sizeof(config.hardfile[2]));
memset(&config.hardfile[3], 0, sizeof(config.hardfile[3]));
result = 1; // We successfully loaded the config.
}
else BootPrint("Config file sanity check failed!\n");
}
else BootPrint("Wrong configuration file format!\n");
}
else printf("Cannot load configuration file\n");
}
else printf("Wrong configuration file size: %lu (expected: %lu)\n", size, sizeof(config));
}
if (!result) {
BootPrint("Can not open configuration file!\n");
BootPrint("Setting config defaults\n");
// set default configuration
memset((void*)&config, 0, sizeof(config)); // Finally found default config bug - params were reversed!
strncpy(config.id, config_id, sizeof(config.id));
strcpy(config.kickstart, "Amiga/KICK.ROM");
config.memory = 0x11;
config.cpu = 0;
config.chipset = 0;
config.floppy.speed = CONFIG_FLOPPY2X;
config.floppy.drives = 1;
config.enable_ide = 0;
config.hardfile[0].enabled = 1;
config.hardfile[0].filename[0] = 0;
config.hardfile[1].enabled = 1;
config.hardfile[1].filename[0] = 0;
updatekickstart = true;
BootPrintEx(">>> No config found. Using defaults. <<<");
}
for (int i = 0; i < 4; i++)
{
df[i].status = 0;
FileClose(&df[i].file);
}
// print config to boot screen
char cfg_str[256];
sprintf(cfg_str, "CPU: %s, Chipset: %s, ChipRAM: %s, FastRAM: %s, SlowRAM: %s",
config_cpu_msg[config.cpu & 0x03], config_chipset_msg[(config.chipset >> 2) & 7],
config_memory_chip_msg[(config.memory >> 0) & 0x03], config_memory_fast_msg[(config.memory >> 4) & 0x03], config_memory_slow_msg[(config.memory >> 2) & 0x03]
);
BootPrintEx(cfg_str);
input_poll(0);
if (is_key_pressed(59))
{
BootPrintEx("Forcing NTSC video ...");
//force NTSC mode if F1 pressed
config.chipset |= CONFIG_NTSC;
}
else if (is_key_pressed(60))
{
BootPrintEx("Forcing PAL video ...");
// force PAL mode if F2 pressed
config.chipset &= ~CONFIG_NTSC;
}
ApplyConfiguration(updatekickstart);
return(result);
}
void MinimigReset()
{
ApplyConfiguration(0);
user_io_rtc_reset();
}
void SetKickstart(char *name)
{
int len = strlen(name);
if (len > (sizeof(config.kickstart) - 1)) len = sizeof(config.kickstart) - 1;
memcpy(config.kickstart, name, len);
config.kickstart[len] = 0;
force_reload_kickstart = 1;
}

View File

@@ -0,0 +1,53 @@
#ifndef __MINIMIG_CONFIG_H__
#define __MINIMIG_CONFIG_H__
#include "../../file_io.h"
typedef struct
{
unsigned char lores;
unsigned char hires;
} filterTYPE;
typedef struct
{
unsigned char speed;
unsigned char drives;
} floppyTYPE;
typedef struct
{
unsigned char enabled;
unsigned char reserved;
char filename[1024];
} hardfileTYPE;
typedef struct
{
char id[8];
unsigned long version;
char kickstart[1024];
filterTYPE filter;
unsigned char memory;
unsigned char chipset;
floppyTYPE floppy;
unsigned char disable_ar3;
unsigned char enable_ide;
unsigned char scanlines;
unsigned char audio;
hardfileTYPE hardfile[4];
unsigned char cpu;
unsigned char autofire;
} configTYPE;
extern configTYPE config;
unsigned char LoadConfiguration(int num);
unsigned char SaveConfiguration(int num);
const char* GetConfigDisplayName(int num);
void MinimigReset();
void SetKickstart(char *name);
#endif

View File

@@ -0,0 +1,672 @@
/*
Copyright 2005, 2006, 2007 Dennis van Weeren
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Minimig is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 2009-11-14 - adapted gap size
// 2009-12-24 - updated sync word list
// - fixed sector header generation
// 2010-01-09 - support for variable number of tracks
#include <stdio.h>
#include <string.h>
#include "../../hardware.h"
#include "../../file_io.h"
#include "minimig_fdd.h"
#include "minimig_config.h"
#include "../../debug.h"
#include "../../fpga_io.h"
#include "../../menu.h"
unsigned char drives = 0; // number of active drives reported by FPGA (may change only during reset)
adfTYPE *pdfx; // drive select pointer
adfTYPE df[4] = { 0 }; // drive information structure
static uint8_t sector_buffer[512];
unsigned char Error;
#define TRACK_SIZE 12668
#define HEADER_SIZE 0x40
#define DATA_SIZE 0x400
#define SECTOR_SIZE (HEADER_SIZE + DATA_SIZE)
#define SECTOR_COUNT 11
#define LAST_SECTOR (SECTOR_COUNT - 1)
#define GAP_SIZE (TRACK_SIZE - SECTOR_COUNT * SECTOR_SIZE)
#define B2W(a,b) (((((uint16_t)(a))<<8) & 0xFF00) | ((uint16_t)(b) & 0x00FF))
// sends the data in the sector buffer to the FPGA, translated into an Amiga floppy format sector
// note that we do not insert clock bits because they will be stripped by the Amiga software anyway
void SendSector(unsigned char *pData, unsigned char sector, unsigned char track, unsigned char dsksynch, unsigned char dsksyncl)
{
unsigned char checksum[4];
unsigned short i;
unsigned char x,y;
unsigned char *p;
// preamble
spi_w(0xAAAA);
spi_w(0xAAAA);
// synchronization
spi_w(B2W(dsksynch, dsksyncl));
spi_w(B2W(dsksynch, dsksyncl));
// odd bits of header
x = 0x55;
checksum[0] = x;
y = track >> 1 & 0x55;
checksum[1] = y;
spi_w(B2W(x,y));
x = sector >> 1 & 0x55;
checksum[2] = x;
y = 11 - sector >> 1 & 0x55;
checksum[3] = y;
spi_w(B2W(x, y));
// even bits of header
x = 0x55;
checksum[0] ^= x;
y = track & 0x55;
checksum[1] ^= y;
spi_w(B2W(x, y));
x = sector & 0x55;
checksum[2] ^= x;
y = 11 - sector & 0x55;
checksum[3] ^= y;
spi_w(B2W(x, y));
// sector label and reserved area (changes nothing to checksum)
i = 0x10;
while (i--) spi_w(0xAAAA);
// send header checksum
spi_w(0xAAAA);
spi_w(0xAAAA);
spi_w(B2W(checksum[0] | 0xAA, checksum[1] | 0xAA));
spi_w(B2W(checksum[2] | 0xAA, checksum[3] | 0xAA));
// calculate data checksum
checksum[0] = 0;
checksum[1] = 0;
checksum[2] = 0;
checksum[3] = 0;
p = pData;
i = DATA_SIZE / 2 / 4;
while (i--)
{
x = *p++;
checksum[0] ^= x ^ x >> 1;
x = *p++;
checksum[1] ^= x ^ x >> 1;
x = *p++;
checksum[2] ^= x ^ x >> 1;
x = *p++;
checksum[3] ^= x ^ x >> 1;
}
// send data checksum
spi_w(0xAAAA);
spi_w(0xAAAA);
spi_w(B2W(checksum[0] | 0xAA, checksum[1] | 0xAA));
spi_w(B2W(checksum[2] | 0xAA, checksum[3] | 0xAA));
// odd bits of data field
i = DATA_SIZE / 4;
p = pData;
while (i--)
{
x = *p++ >> 1 | 0xAA;
y = *p++ >> 1 | 0xAA;
spi_w(B2W(x, y));
}
// even bits of data field
i = DATA_SIZE / 4;
p = pData;
while (i--)
{
x = *p++ | 0xAA;
y = *p++ | 0xAA;
spi_w(B2W(x, y));
}
}
void SendGap(void)
{
unsigned short i = GAP_SIZE/2;
while (i--) spi_w(0xAAAA);
}
// read a track from disk
void ReadTrack(adfTYPE *drive)
{
// track number is updated in drive struct before calling this function
unsigned char sector;
unsigned char status;
unsigned char track;
unsigned short dsksync;
unsigned short dsklen;
uint16_t tmp;
//unsigned short n;
if (drive->track >= drive->tracks)
{
fdd_debugf("Illegal track read: %d\n", drive->track);
drive->track = drive->tracks - 1;
}
unsigned long lba;
if (drive->track != drive->track_prev)
{ // track step or track 0, start at beginning of track
drive->track_prev = drive->track;
sector = 0;
drive->sector_offset = sector;
lba = drive->track * SECTOR_COUNT;
}
else
{ // same track, start at next sector in track
sector = drive->sector_offset;
lba = (drive->track * SECTOR_COUNT) + sector;
}
if (!FileSeekLBA(&drive->file, lba))
{
return;
}
EnableFpga();
tmp = spi_w(0);
status = (uint8_t)(tmp>>8); // read request signal
track = (uint8_t)tmp; // track number (cylinder & head)
dsksync = spi_w(0); // disk sync
dsklen = spi_w(0) & 0x3FFF; // mfm words to transfer
DisableFpga();
if (track >= drive->tracks)
track = drive->tracks - 1;
while (1)
{
FileReadSec(&drive->file, sector_buffer);
EnableFpga();
// check if FPGA is still asking for data
tmp = spi_w(0);
status = (uint8_t)(tmp >> 8); // read request signal
track = (uint8_t)tmp; // track number (cylinder & head)
dsksync = spi_w(0); // disk sync
dsklen = spi_w(0) & 0x3FFF; // mfm words to transfer
if (track >= drive->tracks)
track = drive->tracks - 1;
// workaround for Copy Lock in Wiz'n'Liz and North&South (might brake other games)
if (dsksync == 0x0000 || dsksync == 0x8914 || dsksync == 0xA144)
dsksync = 0x4489;
// North&South: $A144
// Wiz'n'Liz (Copy Lock): $8914
// Prince of Persia: $4891
// Commando: $A245
// some loaders stop dma if sector header isn't what they expect
// because we don't check dma transfer count after sending a word
// the track can be changed while we are sending the rest of the previous sector
// in this case let's start transfer from the beginning
if (track == drive->track)
{
// send sector if fpga is still asking for data
if (status & CMD_RDTRK)
{
//GenerateHeader(sector_header, sector_buffer, sector, track, dsksync);
//SendSector(sector_header, sector_buffer);
SendSector(sector_buffer, sector, track, (unsigned char)(dsksync >> 8), (unsigned char)dsksync);
if (sector == LAST_SECTOR)
SendGap();
}
}
// we are done accessing FPGA
DisableFpga();
// track has changed
if (track != drive->track)
break;
// read dma request
if (!(status & CMD_RDTRK))
break;
sector++;
if (sector >= SECTOR_COUNT)
{
// go to the start of current track
sector = 0;
lba = drive->track * SECTOR_COUNT;
if (!FileSeekLBA(&drive->file, lba))
{
return;
}
}
// remember current sector
drive->sector_offset = sector;
}
}
unsigned char FindSync(adfTYPE *drive)
// reads data from fifo till it finds sync word or fifo is empty and dma inactive (so no more data is expected)
{
unsigned char c1, c2, c3, c4;
unsigned short n;
uint16_t tmp;
while (1)
{
EnableFpga();
tmp = spi_w(0);
c1 = (uint8_t)(tmp >> 8); // write request signal
c2 = (uint8_t)tmp; // track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
if (c2 != drive->track)
break;
spi_w(0); //disk sync word
n = spi_w(0) & 0xBFFF; // mfm words to transfer
if (n == 0)
break;
n &= 0x3FFF;
while (n--)
{
if (spi_w(0) == 0x4489)
{
DisableFpga();
return 1;
}
}
DisableFpga();
}
DisableFpga();
return 0;
}
unsigned char GetHeader(unsigned char *pTrack, unsigned char *pSector)
// this function reads data from fifo till it finds sync word or dma is inactive
{
unsigned char c, c1, c2, c3, c4;
unsigned char i;
unsigned char checksum[4];
uint16_t tmp;
Error = 0;
while (1)
{
EnableFpga();
c1 = (uint8_t)(spi_w(0)>>8); // write request signal, track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
spi_w(0); //disk sync word
tmp = spi_w(0); // mfm words to transfer
if ((tmp & 0x3F00) != 0 || (tmp & 0xFF) > 24)// remaining header data is 25 mfm words
{
tmp = spi_w(0); // second sync
if (tmp != 0x4489)
{
Error = 21;
fdd_debugf("\nSecond sync word missing...\n");
break;
}
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] = c;
c1 = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[1] = c;
c2 = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] = c;
c3 = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[3] = c;
c4 = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
c1 |= c & 0x55;
c = (uint8_t)tmp;
checksum[1] ^= c;
c2 |= c & 0x55;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
c3 |= c & 0x55;
c = (uint8_t)tmp;
checksum[3] ^= c;
c4 |= c & 0x55;
if (c1 != 0xFF) // always 0xFF
Error = 22;
else if (c2 > 159) // Track number (0-159)
Error = 23;
else if (c3 > 10) // Sector number (0-10)
Error = 24;
else if (c4 > 11 || c4 == 0) // Number of sectors to gap (1-11)
Error = 25;
if (Error)
{
fdd_debugf("\nWrong header: %u.%u.%u.%u\n", c1, c2, c3, c4);
break;
}
*pTrack = c2;
*pSector = c3;
for (i = 0; i < 8; i++)
{
tmp = spi_w(0);
checksum[0] ^= (uint8_t)(tmp >> 8);
checksum[1] ^= (uint8_t)tmp;
tmp = spi_w(0);
checksum[2] ^= (uint8_t)(tmp >> 8);
checksum[3] ^= (uint8_t)tmp;
}
checksum[0] &= 0x55;
checksum[1] &= 0x55;
checksum[2] &= 0x55;
checksum[3] &= 0x55;
tmp = (spi_w(0) & 0x5555) << 1;
c1 = (uint8_t)(tmp >> 8);
c2 = (uint8_t)tmp;
tmp = (spi_w(0) & 0x5555) << 1;
c3 = (uint8_t)(tmp >> 8);
c4 = (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c1 |= (uint8_t)(tmp >> 8);
c2 |= (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c3 |= (uint8_t)(tmp >> 8);
c4 |= (uint8_t)tmp;
if (c1 != checksum[0] || c2 != checksum[1] || c3 != checksum[2] || c4 != checksum[3])
{
Error = 26;
break;
}
DisableFpga();
return 1;
}
else if ((tmp & 0x8000) == 0) // not enough data for header and write dma is not active
{
Error = 20;
break;
}
DisableFpga();
}
DisableFpga();
return 0;
}
unsigned char GetData(void)
{
unsigned char c, c1, c2, c3, c4;
unsigned char i;
unsigned char *p;
unsigned short n;
unsigned char checksum[4];
uint16_t tmp;
Error = 0;
while (1)
{
EnableFpga();
c1 = (uint8_t)(spi_w(0) >> 8); // write request signal, track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
spi_w(0);
tmp = spi_w(0); // mfm words to transfer
n = tmp & 0x3FFF;
if (n >= 0x204)
{
tmp = (spi_w(0) & 0x5555) << 1;
c1 = (uint8_t)(tmp >> 8);
c2 = (uint8_t)tmp & 0x55;
tmp = (spi_w(0) & 0x5555) << 1;
c3 = (uint8_t)(tmp >> 8);
c4 = (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c1 |= (uint8_t)(tmp >> 8);
c2 |= (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c3 |= (uint8_t)(tmp >> 8);
c4 |= (uint8_t)tmp;
checksum[0] = 0;
checksum[1] = 0;
checksum[2] = 0;
checksum[3] = 0;
// odd bits of data field
i = 128;
p = sector_buffer;
do
{
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
*p++ = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[1] ^= c;
*p++ = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
*p++ = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[3] ^= c;
*p++ = (c & 0x55) << 1;
} while (--i);
// even bits of data field
i = 128;
p = sector_buffer;
do
{
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
*p++ |= c & 0x55;
c = (uint8_t)tmp;
checksum[1] ^= c;
*p++ |= c & 0x55;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
*p++ |= c & 0x55;
c = (uint8_t)tmp;
checksum[3] ^= c;
*p++ |= c & 0x55;
} while (--i);
checksum[0] &= 0x55;
checksum[1] &= 0x55;
checksum[2] &= 0x55;
checksum[3] &= 0x55;
if (c1 != checksum[0] || c2 != checksum[1] || c3 != checksum[2] || c4 != checksum[3])
{
Error = 29;
break;
}
DisableFpga();
return 1;
}
else if ((tmp & 0x8000) == 0) // not enough data in fifo and write dma is not active
{
Error = 28;
break;
}
DisableFpga();
}
DisableFpga();
return 0;
}
void WriteTrack(adfTYPE *drive)
{
unsigned char Track;
unsigned char Sector;
unsigned long lba = drive->track * SECTOR_COUNT;
drive->track_prev = drive->track + 1; // just to force next read from the start of current track
while (FindSync(drive))
{
if (GetHeader(&Track, &Sector))
{
if (Track == drive->track)
{
if (!FileSeekLBA(&drive->file, lba+Sector))
{
return;
}
if (GetData())
{
if (drive->status & DSK_WRITABLE)
{
FileWriteSec(&drive->file, sector_buffer);
}
else
{
Error = 30;
fdd_debugf("Write attempt to protected disk!\n");
}
}
}
else
Error = 27; //track number reported in sector header is not the same as current drive track
}
if (Error)
{
fdd_debugf("WriteTrack: error %u\n", Error);
ErrorMessage(" WriteTrack", Error);
}
}
}
void UpdateDriveStatus(void)
{
EnableFpga();
spi_w(0x1000 | df[0].status | (df[1].status << 1) | (df[2].status << 2) | (df[3].status << 3));
DisableFpga();
}
void HandleFDD(unsigned char c1, unsigned char c2)
{
unsigned char sel;
drives = (c1 >> 4) & 0x03; // number of active floppy drives
if (c1 & CMD_RDTRK)
{
DISKLED_ON;
sel = (c1 >> 6) & 0x03;
df[sel].track = c2;
ReadTrack(&df[sel]);
DISKLED_OFF;
}
else if (c1 & CMD_WRTRK)
{
DISKLED_ON;
sel = (c1 >> 6) & 0x03;
df[sel].track = c2;
WriteTrack(&df[sel]);
DISKLED_OFF;
}
}
// insert floppy image pointed to to by global <file> into <drive>
void InsertFloppy(adfTYPE *drive, char* path)
{
int writable = FileCanWrite(path);
if (!FileOpenEx(&drive->file, path, writable ? O_RDWR | O_SYNC : O_RDONLY))
{
return;
}
unsigned char i, j;
unsigned long tracks;
// calculate number of tracks in the ADF image file
tracks = drive->file.size / (512 * 11);
if (tracks > MAX_TRACKS)
{
menu_debugf("UNSUPPORTED ADF SIZE!!! Too many tracks: %lu\n", tracks);
tracks = MAX_TRACKS;
}
drive->tracks = (unsigned char)tracks;
strcpy(drive->name, path);
// initialize the rest of drive struct
drive->status = DSK_INSERTED;
if (writable) // read-only attribute
drive->status |= DSK_WRITABLE;
drive->sector_offset = 0;
drive->track = 0;
drive->track_prev = -1;
menu_debugf("Inserting floppy: \"%s\"\n", path);
menu_debugf("file writable: %d\n", writable);
menu_debugf("file size: %lu (%lu KB)\n", drive->file.size, drive->file.size >> 10);
menu_debugf("drive tracks: %u\n", drive->tracks);
menu_debugf("drive status: 0x%02X\n", drive->status);
}

View File

@@ -0,0 +1,35 @@
#ifndef __MINIMIG_FDD_H__
#define __MINIMIG_FDD_H__
#include "../../file_io.h"
// floppy disk interface defs
#define CMD_RDTRK 0x01
#define CMD_WRTRK 0x02
// floppy status
#define DSK_INSERTED 0x01 /*disk is inserted*/
#define DSK_WRITABLE 0x10 /*disk is writable*/
#define MAX_TRACKS (83*2)
typedef struct
{
fileTYPE file;
unsigned char status; /*status of floppy*/
unsigned char tracks; /*number of tracks*/
unsigned char sector_offset; /*sector offset to handle tricky loaders*/
unsigned char track; /*current track*/
unsigned char track_prev; /*previous track*/
char name[1024]; /*floppy name*/
} adfTYPE;
extern unsigned char drives;
extern adfTYPE df[4];
void UpdateDriveStatus(void);
void HandleFDD(unsigned char c1, unsigned char c2);
void InsertFloppy(adfTYPE *drive, char* path);
#endif

View File

@@ -0,0 +1,768 @@
/*
Copyright 2008, 2009 Jakub Bednarski
Copyright 2017, 2018 Sorgelig
This file is part of Minimig
Minimig is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Minimig is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 2009-11-22 - read/write multiple implemented
// 2018-05-13 - 4xIDE implemented
// 2018-05-xx - Use RDB CHS values if valid
// 2018-05-29 - LBA mode implemented
#include <stdio.h>
#include <string.h>
#include "../../hardware.h"
#include "../../file_io.h"
#include "minimig_hdd.h"
#include "../../menu.h"
#include "minimig_config.h"
#include "../../debug.h"
#include "../../fpga_io.h"
#define CMD_IDECMD 0x04
#define CMD_IDEDAT 0x08
#define CMD_IDE_REGS_RD 0x80
#define CMD_IDE_REGS_WR 0x90
#define CMD_IDE_DATA_WR 0xA0
#define CMD_IDE_DATA_RD 0xB0
#define CMD_IDE_STATUS_WR 0xF0
#define IDE_STATUS_END 0x80
#define IDE_STATUS_IRQ 0x10
#define IDE_STATUS_RDY 0x08
#define IDE_STATUS_REQ 0x04
#define IDE_STATUS_ERR 0x01
#define ACMD_RECALIBRATE 0x10
#define ACMD_DIAGNOSTIC 0x90
#define ACMD_IDENTIFY_DEVICE 0xEC
#define ACMD_INITIALIZE_PARAMETERS 0x91
#define ACMD_READ_SECTORS 0x20
#define ACMD_WRITE_SECTORS 0x30
#define ACMD_READ_MULTIPLE 0xC4
#define ACMD_WRITE_MULTIPLE 0xC5
#define ACMD_SET_MULTIPLE_MODE 0xC6
#define SWAP(a) ((((a)&0x000000ff)<<24)|(((a)&0x0000ff00)<<8)|(((a)&0x00ff0000)>>8)|(((a)&0xff000000)>>24))
#define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff))
// hardfile structure
typedef struct
{
int unit;
int enabled;
fileTYPE file;
uint32_t cylinders;
uint16_t heads;
uint16_t sectors;
uint16_t sectors_per_block;
int32_t offset; // if a partition, the lba offset of the partition. Can be negative if we've synthesized an RDB.
uint8_t lu;
int32_t lba, nextlba;
uint16_t sector;
uint16_t cylinder;
uint16_t head;
uint16_t sector_count;
} hdfTYPE;
static hdfTYPE HDF[4] = { 0 };
static uint8_t sector_buffer[512];
static void CalcGeometry(hdfTYPE *hdf)
{
uint32_t head, cyl, spt;
uint32_t sptt[] = { 63, 127, 255, 0 };
uint32_t total = hdf->file.size / 512;
for (int i = 0; sptt[i] != 0; i++)
{
spt = sptt[i];
for (head = 4; head <= 16; head++)
{
cyl = total / (head * spt);
if (total <= 1024 * 1024)
{
if (cyl <= 1023) break;
}
else
{
if (cyl < 16383) break;
if (cyl < 32767 && head >= 5) break;
if (cyl <= 65536) break;
}
}
if (head <= 16) break;
}
hdf->cylinders = cyl;
hdf->heads = (uint16_t)head;
hdf->sectors = (uint16_t)spt;
}
static void GetRDBGeometry(hdfTYPE *hdf)
{
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
hdf->heads = SWAP(rdb->rdb_Heads);
hdf->sectors = SWAP(rdb->rdb_Sectors);
hdf->cylinders = SWAP(rdb->rdb_Cylinders);
if (hdf->sectors > 255 || hdf->heads > 16)
{
printf("ATTN: Illegal CHS value(s).");
if (!(hdf->sectors & 1) && (hdf->sectors < 512) && (hdf->heads <= 8))
{
printf(" Translate: sectors %d->%d, heads %d->%d.\n", hdf->sectors, hdf->sectors / 2, hdf->heads, hdf->heads * 2);
hdf->sectors /= 2;
hdf->heads *= 2;
return;
}
printf(" DANGEROUS: Cannot translate to legal CHS values. Re-calculate the CHS.\n");
CalcGeometry(hdf);
}
}
static void SetHardfileGeometry(hdfTYPE *hdf, int isHDF)
{
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
uint8_t flg = 0;
hdf->offset = 0;
for (int i = 0; i<16; ++i)
{
if (!FileReadSec(&hdf->file, sector_buffer)) break;
for (int i = 0; i < 512; i++) flg |= sector_buffer[i];
if (rdb->rdb_ID == RDB_MAGIC)
{
printf("Found RDB header -> native Amiga image.\n");
GetRDBGeometry(hdf);
return;
}
}
if (isHDF && flg)
{
//use UAE settings.
hdf->heads = 1;
hdf->sectors = 32;
int spc = hdf->heads * hdf->sectors;
hdf->cylinders = hdf->file.size / (512 * spc) + 1;
hdf->offset = -spc;
printf("No RDB header found in HDF image. Assume it's image of single partition. Use Virtual RDB header.\n");
}
else
{
CalcGeometry(hdf);
printf("No RDB header found. Possible non-Amiga or empty image.\n");
}
}
static uint8_t GetDiskStatus(void)
{
uint8_t status;
EnableFpga();
status = (uint8_t)(spi_w(0) >> 8);
spi_w(0);
spi_w(0);
DisableFpga();
return status;
}
static uint32_t RDBChecksum(uint32_t *p, int set)
{
uint32_t count = SWAP(p[1]);
uint32_t result = 0;
if(set) p[2] = 0;
for (uint32_t i = 0; i<count; ++i) result += SWAP(p[i]);
if (!set) return result;
result = 0 - result;
p[2] = SWAP(result);
return 0;
}
// if the HDF file doesn't have a RigidDiskBlock, we synthesize one
static void FakeRDB(hdfTYPE *hdf)
{
int i;
// start by clearing the sector buffer
memset(sector_buffer, 0, 512);
// if we're asked for LBA 0 we create an RDSK block, and if LBA 1, a PART block
switch (hdf->lba)
{
case 0: {
// RDB
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
rdb->rdb_ID = 'R' << 24 | 'D' << 16 | 'S' << 8 | 'K';
rdb->rdb_Summedlongs = 0x40;
rdb->rdb_HostID = 0x07;
rdb->rdb_BlockBytes = 0x200;
rdb->rdb_Flags = 0x12; // (Disk ID valid, no LUNs after this one)
rdb->rdb_BadBlockList = 0xffffffff; // We don't provide a bad block list
rdb->rdb_PartitionList = 1;
rdb->rdb_FileSysHeaderList = 0xffffffff;
rdb->rdb_DriveInit = 0xffffffff;
rdb->rdb_Reserved1[0] = 0xffffffff;
rdb->rdb_Reserved1[1] = 0xffffffff;
rdb->rdb_Reserved1[2] = 0xffffffff;
rdb->rdb_Reserved1[3] = 0xffffffff;
rdb->rdb_Reserved1[4] = 0xffffffff;
rdb->rdb_Reserved1[5] = 0xffffffff;
rdb->rdb_Cylinders = hdf->cylinders;
rdb->rdb_Sectors = hdf->sectors;
rdb->rdb_Heads = hdf->heads;
rdb->rdb_Interleave = 1;
rdb->rdb_Park = rdb->rdb_Cylinders;
rdb->rdb_WritePreComp = rdb->rdb_Cylinders;
rdb->rdb_ReducedWrite = rdb->rdb_Cylinders;
rdb->rdb_StepRate = 3;
rdb->rdb_RDBBlocksLo = 0;
rdb->rdb_RDBBlocksHi = 1;
rdb->rdb_LoCylinder = 1;
rdb->rdb_HiCylinder = rdb->rdb_Cylinders - 1;
rdb->rdb_CylBlocks = rdb->rdb_Heads * rdb->rdb_Sectors;
rdb->rdb_AutoParkSeconds = 0;
rdb->rdb_HighRDSKBlock = 1;
strcpy(rdb->rdb_DiskVendor, "DON'T REPARTITION! 0.00");
uint32_t *p = (uint32_t*)(sector_buffer);
for (int i = 0; i < 40; i++) p[i] = SWAP(p[i]);
RDBChecksum(p, 1);
break;
}
case 1: {
// Partition
struct PartitionBlock *pb = (struct PartitionBlock *)sector_buffer;
pb->pb_ID = 'P' << 24 | 'A' << 16 | 'R' << 8 | 'T';
pb->pb_Summedlongs = 0x40;
pb->pb_HostID = 0x07;
pb->pb_Next = 0xffffffff;
pb->pb_Flags = 0x1; // bootable
pb->pb_DevFlags = 0;
strcpy(pb->pb_DriveName, "0HD\003"); // "DHx" BCPL string
pb->pb_DriveName[0] = hdf->unit + '0';
pb->pb_Environment.de_TableSize = 0x10;
pb->pb_Environment.de_SizeBlock = 0x80;
pb->pb_Environment.de_Surfaces = hdf->heads;
pb->pb_Environment.de_SectorPerBlock = 1;
pb->pb_Environment.de_BlocksPerTrack = hdf->sectors;
pb->pb_Environment.de_Reserved = 2;
pb->pb_Environment.de_LowCyl = 1;
pb->pb_Environment.de_HighCyl = hdf->cylinders - 1;
pb->pb_Environment.de_NumBuffers = 30;
pb->pb_Environment.de_MaxTransfer = 0xffffff;
pb->pb_Environment.de_Mask = 0x7ffffffe;
pb->pb_Environment.de_DosType = 0x444f5301;
uint32_t *p = (uint32_t*)(sector_buffer);
for (int i = 0; i < 64; i++) p[i] = SWAP(p[i]);
RDBChecksum(p, 1);
break;
}
}
}
// builds Identify Device struct
static void IdentifyDevice(uint16_t *pBuffer, hdfTYPE *hdf)
{
char *p, i, x;
uint32_t total_sectors = hdf->cylinders * hdf->heads * hdf->sectors;
memset(pBuffer, 0, 512);
if(hdf->enabled)
{
pBuffer[0] = 1 << 6; // hard disk
pBuffer[1] = hdf->cylinders; // cyl count
pBuffer[3] = hdf->heads; // head count
pBuffer[6] = hdf->sectors; // sectors per track
// FIXME - can get serial no from card itself.
memcpy((char*)&pBuffer[10], "MiniMigHardfile0000 ", 20); // serial number - byte swapped
p = (char*)&pBuffer[27];
if (hdf->offset < 0)
{
memcpy((char*)&pBuffer[23], ".000 ", 8); // firmware version - byte swapped
memcpy(p, "DON'T REPARTITION! ", 40);
}
else
{
memcpy((char*)&pBuffer[23], ".100 ", 8); // firmware version - byte swapped
memcpy(p, "MiSTer ", 40); // model name - byte swapped
p += 8;
char *s = strrchr(config.hardfile[hdf->unit].filename, '/');
if (s) s++;
else s = config.hardfile[hdf->unit].filename;
i = strlen(s);
if (i > 32) s += i - 32;
for (i = 0; (x = s[i]) && i < 16; i++) p[i] = x; // copy file name as model name
}
p = (char*)&pBuffer[27];
for (i = 0; i < 40; i += 2)
{
char c = p[i];
p[i] = p[i + 1];
p[i + 1] = c;
}
}
pBuffer[47] = 0x8010; // maximum sectors per block in Read/Write Multiple command
pBuffer[49] = 1<<9; // LBA support
pBuffer[53] = 1;
pBuffer[54] = hdf->cylinders;
pBuffer[55] = hdf->heads;
pBuffer[56] = hdf->sectors;
pBuffer[57] = (uint16_t)total_sectors;
pBuffer[58] = (uint16_t)(total_sectors >> 16);
}
static void WriteTaskFile(uint8_t error, uint8_t sector_count, uint8_t sector_number, uint8_t cylinder_low, uint8_t cylinder_high, uint8_t drive_head)
{
EnableFpga();
spi_w(CMD_IDE_REGS_WR<<8); // write task file registers command
spi_w(0); // dummy
spi_w(0); // dummy
spi_w(0); // data (dummy)
spi_w(error); // error
spi_w(sector_count); // sector count
spi_w(sector_number); // sector number
spi_w(cylinder_low); // cylinder low
spi_w(cylinder_high); // cylinder high
spi_w(drive_head); // drive/head
DisableFpga();
}
static void WriteStatus(uint8_t status)
{
EnableFpga();
spi_w((CMD_IDE_STATUS_WR<<8) | status);
spi_w(0);
spi_w(0);
DisableFpga();
}
static void ATA_Recalibrate(uint8_t* tfr, hdfTYPE *hdf)
{
// Recalibrate 0x10-0x1F (class 3 command: no data)
hdd_debugf("IDE%d: Recalibrate", hdf->unit);
WriteTaskFile(0, 0, tfr[6] & 0x40 ? 0 : 1, 0, 0, tfr[6] & 0xF0);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
static void ATA_Diagnostic(uint8_t* tfr, hdfTYPE *hdf)
{
// Execute Drive Diagnostic (0x90)
hdd_debugf("IDE: Drive Diagnostic");
WriteTaskFile(1, 0, 0, 0, 0, 0);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
static void ATA_IdentifyDevice(uint8_t* tfr, hdfTYPE *hdf)
{
int i;
// Identify Device (0xec)
hdd_debugf("IDE%d: Identify Device", hdf->unit);
IdentifyDevice((uint16_t*)sector_buffer, hdf);
WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
EnableFpga();
spi_w(CMD_IDE_DATA_WR<<8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
static void ATA_Initialize(uint8_t* tfr, hdfTYPE *hdf)
{
// Initialize Device Parameters (0x91)
hdd_debugf("Initialize Device Parameters");
hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", hdf->unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
static void ATA_SetMultipleMode(uint8_t* tfr, hdfTYPE *hdf)
{
// Set Multiple Mode (0xc6)
hdd_debugf("Set Multiple Mode");
hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", hdf->unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
hdf->sectors_per_block = tfr[2];
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
static int Preface(uint8_t* tfr, hdfTYPE *hdf)
{
hdf->sector = tfr[3];
hdf->cylinder = tfr[4] | (tfr[5] << 8);
hdf->head = tfr[6] & 0x0F;
hdf->lu = tfr[6] & 0xF0;
hdf->sector_count = tfr[2];
if (hdf->sector_count == 0) hdf->sector_count = 256;
uint8_t uselba = hdf->lu & 0x40;
if (uselba)
{
hdf->lba = (hdf->head << 24) | (hdf->cylinder << 8) | hdf->sector;
}
else
{
hdf->lba = hdf->cylinder;
hdf->lba *= hdf->heads;
hdf->lba += hdf->head;
hdf->lba *= hdf->sectors;
hdf->lba += hdf->sector - 1;
}
//printf("setCHS: %s: %d,%d,%d -> %d\n", uselba ? "LBA" : "CHS", hdf->cylinder, hdf->head, hdf->sector, hdf->lba);
hdf->nextlba = hdf->lba;
if (hdf->enabled && hdf->lba >= 0 && hdf->file.size)
{
FileSeekLBA(&hdf->file, (hdf->lba + hdf->offset) < 0 ? 0 : hdf->lba + hdf->offset);
return 1;
}
return 0;
}
static void nextCHS(hdfTYPE *hdf)
{
// do not increment after last sector
if (hdf->sector_count)
{
hdf->nextlba++;
if (hdf->lu & 0x40)
{
hdf->sector = (uint8_t)hdf->nextlba;
hdf->cylinder = (uint16_t)(hdf->nextlba >> 8);
hdf->head = 0xF & (uint8_t)(hdf->nextlba >> 24);
}
else
{
if (hdf->sector == hdf->sectors)
{
hdf->sector = 1;
hdf->head++;
if (hdf->head == hdf->heads)
{
hdf->head = 0;
hdf->cylinder++;
}
}
else
{
hdf->sector++;
}
}
}
}
static void updateTaskFile(hdfTYPE *hdf)
{
WriteTaskFile(0, hdf->sector_count, hdf->sector, (uint8_t)hdf->cylinder, (uint8_t)(hdf->cylinder >> 8), (uint8_t)(hdf->lu | hdf->head));
}
static void ReadSector(hdfTYPE *hdf)
{
// sector outside limit (fake rdb header)
if ((hdf->lba + hdf->offset) < 0)
FakeRDB(hdf);
else
FileReadSec(&hdf->file, sector_buffer);
}
static void SendSector()
{
EnableFpga();
spi_w(CMD_IDE_DATA_WR << 8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
}
static void RecvSector()
{
EnableFpga();
spi_w(CMD_IDE_DATA_RD << 8); // read data command
spi_w(0);
spi_w(0);
spi_block_read_16be((uint16_t*)sector_buffer);
DisableFpga();
}
static void WriteSector(hdfTYPE *hdf)
{
//Do not write to fake RDB header
if ((hdf->lba + hdf->offset) < 0) return;
//Write RDB header, grab the CHS!
if (!hdf->offset && hdf->lba < 16 && (*(uint32_t*)sector_buffer) == RDB_MAGIC)
{
printf("Writing RDB header, LBA=%d: ", hdf->lba);
uint32_t sum = RDBChecksum((uint32_t*)sector_buffer, 0);
if (sum)
{
printf("Checksumm is incorrect(0x%08X)! Ignore the RDB parameters.\n", sum);
}
else
{
GetRDBGeometry(hdf);
printf("Using new CHS: %u/%u/%u (%lu MB)\n", hdf->cylinders, hdf->heads, hdf->sectors, ((((uint32_t)hdf->cylinders) * hdf->heads * hdf->sectors) >> 11));
}
}
FileWriteSec(&hdf->file, sector_buffer);
}
// Read Sectors (0x20)
static void ATA_ReadSectors(uint8_t* tfr, hdfTYPE *hdf)
{
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
if(Preface(tfr, hdf))
{
while (hdf->sector_count)
{
while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer
WriteStatus(IDE_STATUS_IRQ);
ReadSector(hdf);
// to be modified sector of first partition
if (!hdf->unit && !hdf->lba)
{
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
if (rdb->rdb_ID == RDB_MAGIC)
{
// adjust checksum by the difference between old and new flag value
rdb->rdb_ChkSum = SWAP(SWAP(rdb->rdb_ChkSum) + SWAP(rdb->rdb_Flags) - 0x12);
rdb->rdb_Flags = SWAP(0x12);
}
}
SendSector();
hdf->lba++;
hdf->sector_count--;
nextCHS(hdf);
updateTaskFile(hdf);
}
}
WriteStatus(IDE_STATUS_END);
}
// multiple sector transfer per IRQ
static void ATA_ReadMultiple(uint8_t* tfr, hdfTYPE *hdf)
{
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
if (Preface(tfr, hdf))
{
while (hdf->sector_count)
{
while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer
uint16_t block_count = hdf->sector_count;
if (block_count > hdf->sectors_per_block) block_count = hdf->sectors_per_block;
WriteStatus(IDE_STATUS_IRQ);
while (block_count--)
{
ReadSector(hdf);
SendSector();
hdf->lba++;
hdf->sector_count--;
nextCHS(hdf);
}
updateTaskFile(hdf);
}
}
WriteStatus(IDE_STATUS_END);
}
static void ATA_WriteSectors(uint8_t* tfr, hdfTYPE *hdf)
{
WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type
if (Preface(tfr, hdf))
{
hdf->lba += hdf->offset;
while (hdf->sector_count)
{
while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer
RecvSector();
hdf->sector_count--;
nextCHS(hdf);
updateTaskFile(hdf);
WriteStatus(hdf->sector_count ? IDE_STATUS_IRQ : IDE_STATUS_END | IDE_STATUS_IRQ);
WriteSector(hdf);
hdf->lba++;
}
}
}
static void ATA_WriteMultiple(uint8_t* tfr, hdfTYPE *hdf)
{
WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type
if (Preface(tfr, hdf))
{
hdf->lba += hdf->offset;
while (hdf->sector_count)
{
uint16_t block_count = hdf->sector_count;
if (block_count > hdf->sectors_per_block) block_count = hdf->sectors_per_block;
while (block_count)
{
while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer
RecvSector();
WriteSector(hdf);
hdf->lba++;
block_count--;
hdf->sector_count--;
nextCHS(hdf);
}
updateTaskFile(hdf);
WriteStatus(hdf->sector_count ? IDE_STATUS_IRQ : IDE_STATUS_END | IDE_STATUS_IRQ);
}
}
}
void HandleHDD(uint8_t c1, uint8_t c2)
{
if (c1 & CMD_IDECMD)
{
uint8_t unit = 0;
uint8_t tfr[8];
DISKLED_ON;
EnableFpga();
spi_w(CMD_IDE_REGS_RD<<8); // read task file registers
spi_w(0);
spi_w(0);
for (int i = 0; i < 8; i++)
{
uint16_t tmp = spi_w(0);
tfr[i] = (uint8_t)tmp;
if (i == 6) unit = ((tmp >> 7) & 2) | ((tmp >> 4) & 1);
}
DisableFpga();
//printf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
hdfTYPE *hdf = &HDF[unit];
if (hdf->enabled)
{
if ((tfr[7] & 0xF0) == ACMD_RECALIBRATE) ATA_Recalibrate (tfr, hdf);
else if (tfr[7] == ACMD_DIAGNOSTIC) ATA_Diagnostic (tfr, hdf);
else if (tfr[7] == ACMD_IDENTIFY_DEVICE) ATA_IdentifyDevice (tfr, hdf);
else if (tfr[7] == ACMD_INITIALIZE_PARAMETERS) ATA_Initialize (tfr, hdf);
else if (tfr[7] == ACMD_SET_MULTIPLE_MODE) ATA_SetMultipleMode (tfr, hdf);
else if (tfr[7] == ACMD_READ_SECTORS) ATA_ReadSectors (tfr, hdf);
else if (tfr[7] == ACMD_READ_MULTIPLE) ATA_ReadMultiple (tfr, hdf);
else if (tfr[7] == ACMD_WRITE_SECTORS) ATA_WriteSectors (tfr, hdf);
else if (tfr[7] == ACMD_WRITE_MULTIPLE) ATA_WriteMultiple (tfr, hdf);
else
{
printf("Unknown ATA command: IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
WriteTaskFile(0x04, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ | IDE_STATUS_ERR);
}
}
else
{
printf("IDE%d not enabled: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
WriteTaskFile(0x04, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ | IDE_STATUS_ERR);
}
DISKLED_OFF;
}
}
uint8_t OpenHardfile(uint8_t unit)
{
hdfTYPE *hdf = &HDF[unit];
hdf->unit = unit;
hdf->enabled = 0;
if (config.enable_ide && config.hardfile[unit].enabled)
{
printf("\nChecking HDD %d\n", unit);
if (config.hardfile[unit].filename[0])
{
if (FileOpenEx(&hdf->file, config.hardfile[unit].filename, FileCanWrite(config.hardfile[unit].filename) ? O_RDWR : O_RDONLY))
{
hdf->enabled = 1;
printf("file: \"%s\": ", hdf->file.name);
SetHardfileGeometry(hdf, !strcasecmp(".hdf", config.hardfile[unit].filename + strlen(config.hardfile[unit].filename) - 4));
printf("size: %llu (%llu MB)\n", hdf->file.size, hdf->file.size >> 20);
printf("CHS: %u/%u/%u", hdf->cylinders, hdf->heads, hdf->sectors);
printf(" (%lu MB), ", ((((uint32_t)hdf->cylinders) * hdf->heads * hdf->sectors) >> 11));
printf("Offset: %d\n", hdf->offset);
return 1;
}
}
printf("HDD %d: not present\n", unit);
}
// close if opened earlier.
FileClose(&hdf->file);
return 0;
}
int checkHDF(const char* name, struct RigidDiskBlock **rdb)
{
fileTYPE file = { 0 };
*rdb = NULL;
if (FileOpenEx(&file, name, O_RDONLY))
{
*rdb = (struct RigidDiskBlock *)sector_buffer;
for (int i = 0; i<16; ++i)
{
if (!FileReadSec(&file, sector_buffer)) break;
if ((*rdb)->rdb_ID == RDB_MAGIC)
{
FileClose(&file);
(*rdb)->rdb_Heads = SWAP((*rdb)->rdb_Heads);
(*rdb)->rdb_Sectors = SWAP((*rdb)->rdb_Sectors);
(*rdb)->rdb_Cylinders = SWAP((*rdb)->rdb_Cylinders);
return ((*rdb)->rdb_Heads <= 16 && (*rdb)->rdb_Sectors <= 255 && (*rdb)->rdb_Cylinders <= 65536);
}
}
FileClose(&file);
return 1; // non-HDF file
}
return 0;
}

View File

@@ -0,0 +1,13 @@
// hdd.h
#ifndef __MINIMIG_HDD_H__
#define __MINIMIG_HDD_H__
#include "minimig_hdd_internal.h"
// functions
void HandleHDD(uint8_t c1, uint8_t c2);
uint8_t OpenHardfile(uint8_t unit);
int checkHDF(const char* name, struct RigidDiskBlock **rdb);
#endif // __HDD_H__

View File

@@ -0,0 +1,90 @@
#ifndef __MINIMIG_HDD_INTERNAL_H__
#define __MINIMIG_HDD_INTERNAL_H__
// Structure definitions for RDB emulation.
// For hardfiles that have no RDB information, we'll just create a single-partition RDB and Part block
// on blocks 0 and 1. All other blocks within the first cylinder will be translated into the hardfile
#define RDB_MAGIC 0x4B534452 // "RDSK"
struct RigidDiskBlock {
unsigned long rdb_ID; // "RDSK"
unsigned long rdb_Summedlongs; // 0x40
long rdb_ChkSum; // Sum to zero
unsigned long rdb_HostID; // 0x07
unsigned long rdb_BlockBytes; // 0x200
unsigned long rdb_Flags; // 0x12 (Disk ID valid, no LUNs after this one)
unsigned long rdb_BadBlockList; // -1 since we don't provide one
unsigned long rdb_PartitionList; // 1
unsigned long rdb_FileSysHeaderList; // -1
unsigned long rdb_DriveInit; // -1
unsigned long rdb_Reserved1[6]; // 0xffffffff
unsigned long rdb_Cylinders;
unsigned long rdb_Sectors;
unsigned long rdb_Heads;
unsigned long rdb_Interleave; // 1
unsigned long rdb_Park; // =Cylinder count
unsigned long rdb_Reserved2[3];
unsigned long rdb_WritePreComp; // High cylinder ?
unsigned long rdb_ReducedWrite; // High cylinder ?
unsigned long rdb_StepRate; // 3 ?
unsigned long rdb_Reserved3[5];
unsigned long rdb_RDBBlocksLo; // block zero
unsigned long rdb_RDBBlocksHi; // block one
unsigned long rdb_LoCylinder; // 1
unsigned long rdb_HiCylinder; // From the hardfile: cylinder count -1
unsigned long rdb_CylBlocks; // From the hardfile: heads * sectors
unsigned long rdb_AutoParkSeconds; // zero
unsigned long rdb_HighRDSKBlock; // 1
unsigned long rdb_Reserved4;
char rdb_DiskVendor[8]; // "Don't"
char rdb_DiskProduct[16]; // " repartition!"
char rdb_DiskRevision[4];
char rdb_ControllerVendor[8];
char rdb_ControllerProduct[16];
char rdb_ControllerRevision[4];
unsigned long rdb_Reserved5[10];
} __attribute__((packed));
struct DosEnvec {
unsigned long de_TableSize; // Size of Environment vector - 0x10
unsigned long de_SizeBlock; // in longwords - 0x80
unsigned long de_SecOrg; // 0
unsigned long de_Surfaces; // Heads?
unsigned long de_SectorPerBlock; // 1
unsigned long de_BlocksPerTrack;
unsigned long de_Reserved; // 2 ?
unsigned long de_PreAlloc; // 0
unsigned long de_Interleave; // 0
unsigned long de_LowCyl;
unsigned long de_HighCyl;
unsigned long de_NumBuffers; // 30
unsigned long de_BufMemType; // 0 - any available
unsigned long de_MaxTransfer; // 0x00ffffff
unsigned long de_Mask; // 0x7ffffffe
long de_BootPri; // 0
unsigned long de_DosType; // 0x444f5301 or 3
// Extra fields
unsigned long de_Baud;
unsigned long de_Control;
unsigned long de_BootBlocks;
} __attribute__((packed));
struct PartitionBlock {
unsigned long pb_ID; // "PART"
unsigned long pb_Summedlongs; // 0x40
long pb_ChkSum; // Sum to zero
unsigned long pb_HostID; // 0x07
unsigned long pb_Next; // -1
unsigned long pb_Flags; // 1 - Bootable
unsigned long pb_Reserved1[2]; // 0
unsigned long pb_DevFlags; // 0
char pb_DriveName[32]; // 0x03"DH0"
unsigned long pb_Reserved2[15];
struct DosEnvec pb_Environment;
unsigned long pb_EReserved[12]; /* reserved for future environment vector */
} __attribute__((packed));
#endif /* HDD_INTERNAL_H */