Alpha release candidate rc1

This commit is contained in:
Philip Smart
2023-04-17 21:46:26 +01:00
parent 2200e233e2
commit b0ad5db6c6
59 changed files with 12616 additions and 10889 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,15 +1,17 @@
# Select the target host.
#MODEL := MZ2000
#MODEL := MZ700
#DEBUG := y
MODEL := MZ80A
#MODEL := MZ2000
#MODEL := MZ700
#MODEL := MZ80A
#MODEL := PCW8XXX
#MODEL := PCW9XXX
KERNEL := $(PWD)/../../../linux/kernel
FUSIONX := $(PWD)/../..
CROSS := arm-linux-gnueabihf-
CTRLINC = -IZeta/API -IZ80/API -DTARGET_HOST_$(MODEL)=1
ccflags-y = -O2 -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ -DTARGET_HOST_$(MODEL)=1
ifeq ($(DEBUG),y)
ccflags-y += -DTTYMZ_DEBUG
else
ccflags-y += -O2 -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__
endif
obj-m += ttymzdrv.o
ttymzdrv-objs += ttymz.o z80io.o sharpmz.o
@@ -18,16 +20,42 @@ ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o
all:
@echo "Specify target host, ie. make <host>"
@echo "Supported hosts: MZ80A, MZ700, MZ2000, PCW8XXX, PCW9XXX"
all:
MZ80A: MODEL_MZ80A
MZ700: MODEL_MZ700
MZ2000: MODEL_MZ2000
PCW8XXX: MODEL_PCW8XXX
PCW9XXX: MODEL_PCW9XXX
MODEL_MZ80A:
$(MAKE) MODEL=MZ80A BUILD_MZ80A
MODEL_MZ700:
$(MAKE) MODEL=MZ700 BUILD_MZ700
MODEL_MZ2000:
$(MAKE) MODEL=MZ2000 BUILD_MZ2000
MODEL_PCW8XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW8XXX
MODEL_PCW9XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW9XXX
BUILD_MZ80A: kmod
BUILD_MZ700: kmod
BUILD_MZ2000: kmod
BUILD_PCW8XXX: kmod
BUILD_PCW9XXX: kmod
kmod:
@echo ""
@echo "Build TTYMZ driver for host: $(MODEL)"
make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules
@echo ""
install:
@echo "Copy kernel driver..."
@cp ttymz.ko $(FUSIONX)/modules/
@cp ttymzdrv.ko $(FUSIONX)/modules/
clean:
make -C $(KERNEL) M=$(PWD) clean
@rm -f ttymz

View File

@@ -542,21 +542,21 @@ static t_scanCodeMap scanCodeMap[] = {
CTRL_G , // ^G F7
CTRL_H , // ^H F8
// S5 28 - 2F
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
HOTKEY_ORIGINAL, // 1 - Hotkey to invoke original mode.
HOTKEY_RFS40, // 2 - Hotkey to invoke RFS 40 mode.
HOTKEY_TZFS, // 3 - Hotkey to invoke TZFS mode.
HOTKEY_LINUX, // 4 - Hotkey to invoke Linux mode.
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
// S6 30 - 37 (ERROR? 7 VALUES ONLY!!)
NOKEY , // ^YEN E6
CTRL_CAPPA , // ^ EF
NOKEY , // ^YEN E6
CTRL_CAPPA , // ^ EF
NOKEY ,
NOKEY ,
NOKEY ,
CTRL_UNDSCR , // ^,
CTRL_UNDSCR , // ^,
NOKEY ,
NOKEY ,
// S7 - 38 - 3F
@@ -567,22 +567,22 @@ static t_scanCodeMap scanCodeMap[] = {
NOKEY ,
NOKEY ,
NOKEY ,
CTRL_SLASH , // ^/ EE
CTRL_SLASH , // ^/ EE
// S8 40 - 47 - modifier keys.
NOKEY , // BREAK - CTRL+BREAK - not yet assigned
NOKEY , // CTRL
NOKEY , // BREAK - CTRL+BREAK - not yet assigned
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY , // SHIFT
NOKEY , // SHIFT
// S9 48 - 4F - Function keys.
FUNC1 , // Function key F1
FUNC2 , // Function key F2
FUNC3 , // Function key F3
FUNC4 , // Function key F4
FUNC5 , // Function key F5
FUNC1 , // Function key F1
FUNC2 , // Function key F2
FUNC3 , // Function key F3
FUNC4 , // Function key F4
FUNC5 , // Function key F5
NOKEY ,
NOKEY ,
NOKEY
@@ -681,8 +681,8 @@ static t_scanCodeMap scanCodeMap[] = {
NOKEY
}}
};
#endif
#if (TARGET_HOST_MZ80A == 1)
#elif (TARGET_HOST_MZ80A == 1)
static t_scanCodeMap scanCodeMap[] = {
// MZ_80A NO SHIFT
{{
@@ -1151,6 +1151,477 @@ static t_scanCodeMap scanCodeMap[] = {
}}
};
#elif (TARGET_HOST_MZ2000 == 1)
static t_scanCodeMap scanCodeMap[] = {
// MZ_2000 NO SHIFT
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
GRAPHKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'2' , // 2
'1' , // 1
'w' , // w
'q' , // q
'a' , // a
BACKS , // DELETE
NOKEY , // NULL
'z' , // z
// S2 10 - 17
'4' , // 4
'3' , // 3
'r' , // r
'e' , // e
'd' , // d
's' , // s
'x' , // x
'c' , // c
// S3 18 - 1F
'6' , // 6
'5' , // 5
'y' , // y
't' , // t
'g' , // g
'f' , // f
'v' , // v
'b' , // b
// S4 20 - 27
'8' , // 8
'7' , // 7
'i' , // i
'u' , // u
'j' , // j
'h' , // h
'n' , // n
' ' , // SPACE
// S5 28 - 2F
'0' , // 0
'9' , // 9
'p' , // p
'o' , // o
'l' , // l
'k' , // k
',' , // ,
'm' , // m
// S6 30 - 37
'^' , // ^
'-' , // -
'[' , // [
'@' , // @
':' , // :
';' , // ;
'/' , // /
'.' , // .
// S7 38 - 3F
HOMEKEY , // HOME.
'\\' , // Backslash
CURSRIGHT, // CURSOR RIGHT
CURSUP , // CURSOR UP
CR , // CR
']' , // ]
NOKEY , //
'?' , // ?
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 CAPS LOCK
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'2' , // 2
'1' , // 1
'W' , // W
'Q' , // Q
'A' , // A
BACKS , // DELETE
NOKEY , // NULL
'Z' , // Z
// S2 10 - 17
'4' , // 4
'3' , // 3
'R' , // R
'E' , // E
'D' , // D
'S' , // S
'X' , // X
'C' , // C
// S3 18 - 1F
'6' , // 6
'5' , // 5
'Y' , // Y
'T' , // T
'G' , // G
'F' , // F
'V' , // V
'B' , // B
// S4 20 - 27
'8' , // 8
'7' , // 7
'I' , // I
'U' , // U
'J' , // J
'H' , // H
'N' , // N
' ' , // SPACE
// S5 28 - 2F
'0' , // 0
'9' , // 9
'P' , // P
'O' , // O
'L' , // L
'K' , // K
',' , // ,
'M' , // M
// S6 30 - 37
'^' , // ^
'-' , // -
'[' , // [
'@' , // @
':' , // :
';' , // ;
'/' , // /
'.' , // .
// S7 38 - 3F
HOMEKEY , // HOME.
'\\' , // Backslash
CURSRIGHT, // CURSOR RIGHT
CURSUP , // CURSOR UP
CR , // CR
']' , // ]
NOKEY , //
'?' , // ?
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 SHIFT LOCK.
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'"' , // "
'!' , // !
'W' , // W
'Q' , // Q
'A' , // A
INSERT , // INSERT
NOKEY , // NULL
'Z' , // Z
// S2 10 - 17
'$' , // $
'#' , // #
'R' , // R
'E' , // E
'D' , // D
'S' , // S
'X' , // X
'C' , // C
// S3 18 - 1F
'&' , // &
'%' , // %
'Y' , // Y
'T' , // T
'G' , // G
'F' , // F
'V' , // V
'B' , // B
// S4 20 - 27
'(' , // (
'\'' , // '
'I' , // I
'U' , // U
'J' , // J
'H' , // H
'N' , // N
' ' , // SPACE
// S5 28 - 2F
'_' , // _
')' , // )
'P' , // P
'O' , // O
'L' , // L
'K' , // K
'<' , // <
'M' , // M
// S6 30 - 37
'~' , // ~
'=' , // =
'{' , // {
'`' , // `
'*' , // *
'+' , // +
NOKEY , //
'>' , // >
// S7 38 - 3F
CLRKEY , // CLR.
'|' , // |
CURSLEFT , // CURSOR LEFT
CURSDOWN , // CURSOR DOWN
CR , // CR
'}' , // }
NOKEY , //
NOKEY , //
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 CONTROL CODE
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAGRAPHKEY, // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
NOKEY , //
NOKEY , //
CTRL_W , // CTRL_W
CTRL_Q , // CTRL_Q
CTRL_A , // CTRL_A
DELETE , // DELETE
NOKEY , // NULL
CTRL_Z , // CTRL_Z
// S2 10 - 17
NOKEY , //
NOKEY , //
CTRL_R , // CTRL_R
CTRL_E , // CTRL_E
CTRL_D , // CTRL_D
CTRL_S , // CTRL_S
CTRL_X , // CTRL_X
CTRL_C , // CTRL_C
// S3 18 - 1F
NOKEY , //
NOKEY , //
CTRL_Y , // CTRL_Y
CTRL_T , // CTRL_T
CTRL_G , // CTRL_G
CTRL_F , // CTRL_F
CTRL_V , // CTRL_V
CTRL_B , // CTRL_B
// S4 20 - 27
NOKEY , //
NOKEY , //
CTRL_I , // CTRL_I
CTRL_U , // CTRL_U
CTRL_J , // CTRL_J
CTRL_H , // CTRL_H
CTRL_N , // CTRL_N
' ' , // SPACE
// S5 28 - 2F
CTRL_UNDSCR, // CTRL+_
NOKEY , //
CTRL_P , // CTRL_P
CTRL_O , // CTRL_O
CTRL_L , // CTRL_L
CTRL_K , // CTRL_K
NOKEY , //
CTRL_M , // CTRL_M
// S6 30 - 37
CTRL_CAPPA, // CTRL+^
NOKEY , //
CTRL_LB , // CTRL+[
CTRL_AT , // CTRL+@
NOKEY , //
NOKEY , //
CTRL_SLASH, // CTRL+/
NOKEY , //
// S7 38 - 3F
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
CTRL_RB , // CTRL+]
NOKEY , //
NOKEY , //
// S8 40 - 47 - Keypad keys.
NOKEY , // Keypad 8
NOKEY , // 7
NOKEY , // 5
HOTKEY_LINUX, // 4 - Hotkey to invoke Linux mode.
HOTKEY_RFS40, // 2 - Hotkey to invoke RFS 40 mode.
HOTKEY_RFS80, // 1 - Hotkey to invoke RFS 80 mode.
NOKEY , // 00
HOTKEY_ORIGINAL, // 0 - Hotkey to invoke original mode.
// S9 48 - 4F - Keypad keys.
NOKEY , // +
NOKEY , // 9
NOKEY , // -
NOKEY , // 6
NOKEY , //
HOTKEY_TZFS, // 3 - Hotkey to invoke TZFS mode.
NOKEY ,
NOKEY // .
}},
// MZ_2000 KANA
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
GRAPHKEY , // DAKU TEN
NOKEY , //
// S1 08 - 0F
0x35 , // HA
0x77 , // TA
0xD7 , // WA
0xB3 , // YO
0xB7 , // HANDAKU
NOKEY ,
NOKEY ,
NOKEY ,
// S2 10 - 17
0x7C , // KA
0x70 , // KE
0x41 , // SHI
0x31 , // KO
0x39 , // HI
0xA6 , // TE
0x78 , // KI
0xDD , // CHI
// S3 18 - 1F
0x3D , // FU
0x5D , // MI
0x6C , // MU
0x56 , // ME
0x1D , // RHI
0x33 , // RA
0xD5 , // HE
0xB1 , // HO
// S4 20 - 27
0x46 , // SA
0x6E , // TO
0xD9 , // THU
0x48 , // SU
0x74 , // KU
0x43 , // SE
0x4C , // SO
0x73 , // MA
// S5 28 - 2F
0x3F , // A
0x36 , // I
0x7E , // U
0x3B , // E
0x7A , // O
0x1E , // NA
0x5F , // NI
0xA2 , // NU
// S6 30 - 37
0xD3 , // YO
0x9F , // YU
0xD1 , // YA
0x00 , // SPACE
0x9D , // NO
0xA3 , // NE
0xD0 , // RU
0xB9 , // RE
// S7 38 - 3F
0xC6 , // ?CLR
0xC5 , // ?HOME
0xC2 , // ?CURSOR UP
0xC1 , // ?CURSOR DOWN
0xC3 , // ?CURSOR RIGHT
0xC4 , // ?CURSOR LEFT
0xBB , // DASH
0xBE , // RO
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}}
};
#else
#error "Unknown host, no keyboard map configured."
#endif
// Mapping table of sharp special control keys to ANSI ESCape sequences.
@@ -1203,8 +1674,36 @@ static t_ansiKeyMap ansiKeySeq[] = {
// Colour map for the Ansi Terminal.
const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
#endif
#if (TARGET_HOST_MZ80A == 1)
#elif (TARGET_HOST_MZ80A == 1)
// Static structures for controlling and managing hardware features.
// Display control structure. Used to manage the display including the Ansi Terminal.
const t_displayBuffer displayDefault = { .displayAttr = 0x71, .backingRow = 0, .displayCol = 0, .displayRow = 0, .maxBackingRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS),
.maxDisplayRow = VC_MAX_ROWS, .maxBackingCol = 80, .useAnsiTerm = 1, .lineWrap = 0, .inDebug = 0
};
// Keyboard control structure. Used to manage keyboard sweep, mapping and store.
const t_keyboard keyboardDefault = { .holdTimer = 0L, .autorepeat = 0, .mode = KEYB_LOWERCASE, .cursorOn = 1, .flashTimer = 0L, .keyBuf[0] = 0x00, .keyBufPtr = 0,
.mode = KEYB_LOWERCASE, .dualmode = KEYB_DUAL_GRAPH
};
// Audio control structure. Used to manage audio output.
const t_audio audioDefault = { .audioStopTimer = 0
};
// AnsiTerminal control structure. Used to manage the inbuilt Ansi Terminal.
const t_AnsiTerm ansitermDefault = { .state = ANSITERM_ESC, .charcnt = 0, .paramcnt = 0, .setDisplayMode = 0, .setExtendedMode = 0, .saveRow = 0, .saveCol = 0,
};
// Module control structure.
const t_control ctrlDefault = { .suspendIO = 0, .debug = 0
};
// Colour map for the Ansi Terminal.
const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
#elif (TARGET_HOST_MZ2000 == 1)
// Static structures for controlling and managing hardware features.
// Display control structure. Used to manage the display including the Ansi Terminal.
const t_displayBuffer displayDefault = { .displayAttr = 0x71, .backingRow = 0, .displayCol = 0, .displayRow = 0, .maxBackingRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS),
@@ -1249,6 +1748,11 @@ static t_ansiKeyMap ansiKeySeq[] = {
//
uint8_t mzInitMBHardware(void)
{
#if (TARGET_HOST_MZ700 == 1)
// Ensure memory paging is set to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// From the 1Z-013A monitor code, initialise the 8255 PIO.
//
WRITE_HARDWARE(1, MBADDR_KEYPF, 0x8A); // 10001010 CTRL WORD MODE0
@@ -1312,10 +1816,11 @@ void mzBeep(uint32_t freq, uint32_t timeout)
// Locals.
#if (TARGET_HOST_MZ80A == 1)
uint16_t freqDiv = TIMER_8253_MZ80A_FREQ/(freq*2);
#else
#elif (TARGET_HOST_MZ700 == 1)
uint16_t freqDiv = TIMER_8253_MZ700_FREQ/freq;
#endif
#if (TARGET_HOST_MZ2000 == 0)
// Setup the 8253 Timer 0 to output a sound, enable output to amplifier and set timeout.
WRITE_HARDWARE(0, MBADDR_CONTF, 0x34 ); // Timer 0 to square wave generator, load LSB first.
WRITE_HARDWARE(0, MBADDR_CONT0, (freqDiv&0xff) );
@@ -1324,6 +1829,7 @@ void mzBeep(uint32_t freq, uint32_t timeout)
// Set a 500ms timeout on the sound to create beep effect.
audio.audioStopTimer = timeout == 0 ? 11 : (timeout/10)+1; // Each unit is 10ms, valid range 1..n
#endif
return;
}
@@ -2964,8 +3470,33 @@ uint8_t mzSweepKeys(void)
{
keyboard.shiftKey = 0;
}
#endif
#if (TARGET_HOST_MZ80A == 1)
#elif (TARGET_HOST_MZ80A == 1)
// Check for modifiers.
//
if((keyboard.scanbuf[0][0] & 0x01) == 0)
{
keyboard.shiftKey = 1;
} else
{
keyboard.shiftKey = 0;
}
if((keyboard.scanbuf[0][0] & 0x80) == 0 && keyboard.shiftKey == 0)
{
keyboard.ctrlKey = 1;
} else
{
keyboard.ctrlKey = 0;
}
if((keyboard.scanbuf[0][0] & 0x80) == 0 && keyboard.shiftKey == 1)
{
keyboard.breakKey = 1;
} else
{
keyboard.breakKey = 0;
}
#elif (TARGET_HOST_MZ2000 == 1)
// Check for modifiers.
//
if((keyboard.scanbuf[0][0] & 0x01) == 0)

View File

@@ -36,182 +36,210 @@
extern "C" {
#endif
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 1 // MZ80A
// Build time target. Overrides if compile time definition given.
#if defined(TARGET_HOST_MZ700)
#define TARGET_HOST_MZ700 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ2000)
#define TARGET_HOST_MZ2000 1
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ80A)
#define TARGET_HOST_MZ80A 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_PCW8XXX) || defined(TARGET_HOST_PCW9XXX)
#define TARGET_HOST_PCW 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#else
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 0 // MZ80A
#define TARGET_HOST_PCW 0 // Amstrad PCW8XXX/9XXX
#endif
// Video display constants.
#define VC_MAX_ROWS 25 // Maximum number of rows on display.
#define VC_MAX_COLUMNS 80 // Maximum number of columns on display.
#define VC_MAX_BUFFER_ROWS 50 // Maximum number of backing store rows for scrollback feature.
#define VC_DISPLAY_BUFFER_SIZE VC_MAX_COLUMNS * VC_MAX_BUFFER_ROWS // Size of the display buffer for scrollback.
#define VC_MAX_ROWS 25 // Maximum number of rows on display.
#if defined(TARGET_HOST_MZ700)
#define VC_MAX_COLUMNS 40 // Maximum number of columns on display.
#else
#define VC_MAX_COLUMNS 80 // Maximum number of columns on display.
#endif
#define VC_MAX_BUFFER_ROWS 50 // Maximum number of backing store rows for scrollback feature.
#define VC_DISPLAY_BUFFER_SIZE VC_MAX_COLUMNS * VC_MAX_BUFFER_ROWS // Size of the display buffer for scrollback.
// Keyboard constants.
#define KEYB_AUTOREPEAT_INITIAL_TIME 800 // Time in milliseconds before starting autorepeat.
#define KEYB_AUTOREPEAT_TIME 100 // Time in milliseconds between auto repeating characters.
#define KEYB_FLASH_TIME 350 // Time in milliseconds for the cursor flash change.
#define CURSOR_THICK_BLOCK 0x43 // Thick block cursor for lower case CAPS OFF
#define CURSOR_BLOCK 0xEF // Block cursor for SHIFT Lock.
#define CURSOR_UNDERLINE 0x3E // Thick underscore for CAPS Lock.
#define MAX_KEYB_BUFFER_SIZE 32 // Maximum size of the keyboard buffer.
#define KEYB_AUTOREPEAT_INITIAL_TIME 800 // Time in milliseconds before starting autorepeat.
#define KEYB_AUTOREPEAT_TIME 100 // Time in milliseconds between auto repeating characters.
#define KEYB_FLASH_TIME 350 // Time in milliseconds for the cursor flash change.
#define CURSOR_THICK_BLOCK 0x43 // Thick block cursor for lower case CAPS OFF
#define CURSOR_BLOCK 0xEF // Block cursor for SHIFT Lock.
#define CURSOR_UNDERLINE 0x3E // Thick underscore for CAPS Lock.
#define MAX_KEYB_BUFFER_SIZE 32 // Maximum size of the keyboard buffer.
// Audio constants.
#define TIMER_8253_MZ80A_FREQ 2000000 // Base input frequency of Timer 0 for square wave generation.
#define TIMER_8253_MZ700 768000 // Base input frequency of Timer 0 for square wave generation.
#define TIMER_8253_MZ80A_FREQ 2000000 // Base input frequency of Timer 0 for square wave generation.
#define TIMER_8253_MZ700_FREQ 768000 // Base input frequency of Timer 0 for square wave generation.
// Base addresses and sizes within the Video Controller.
#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller.
#define VIDEO_VRAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D000 // Base address of the character video RAM using direct addressing.
#define VIDEO_VRAM_SIZE 0x800 // Size of the video RAM.
#define VIDEO_ARAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D800 // Base address of the character attribute RAM using direct addressing.
#define VIDEO_ARAM_SIZE 0x800 // Size of the attribute RAM.
#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller.
#define VIDEO_VRAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D000 // Base address of the character video RAM using direct addressing.
#define VIDEO_VRAM_SIZE 0x800 // Size of the video RAM.
#define VIDEO_ARAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D800 // Base address of the character attribute RAM using direct addressing.
#define VIDEO_ARAM_SIZE 0x800 // Size of the attribute RAM.
// Video Module control bits.
#define VMMODE_MASK 0xF8 // Mask to mask out video mode.
#define VMMODE_MZ80K 0x00 // Video mode = MZ80K
#define VMMODE_MZ80C 0x01 // Video mode = MZ80C
#define VMMODE_MZ1200 0x02 // Video mode = MZ1200
#define VMMODE_MZ80A 0x03 // Video mode = MZ80A
#define VMMODE_MZ700 0x04 // Video mode = MZ700
#define VMMODE_MZ800 0x05 // Video mode = MZ800
#define VMMODE_MZ1500 0x06 // Video mode = MZ1500
#define VMMODE_MZ80B 0x07 // Video mode = MZ80B
#define VMMODE_MZ2000 0x08 // Video mode = MZ2000
#define VMMODE_MZ2200 0x09 // Video mode = MZ2200
#define VMMODE_MZ2500 0x0A // Video mode = MZ2500
#define VMMODE_80CHAR 0x80 // Enable 80 character display.
#define VMMODE_80CHAR_MASK 0x7F // Mask to filter out display width control bit.
#define VMMODE_COLOUR 0x20 // Enable colour display.
#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit.
#define VMMODE_MASK 0xF8 // Mask to mask out video mode.
#define VMMODE_MZ80K 0x00 // Video mode = MZ80K
#define VMMODE_MZ80C 0x01 // Video mode = MZ80C
#define VMMODE_MZ1200 0x02 // Video mode = MZ1200
#define VMMODE_MZ80A 0x03 // Video mode = MZ80A
#define VMMODE_MZ700 0x04 // Video mode = MZ700
#define VMMODE_MZ800 0x05 // Video mode = MZ800
#define VMMODE_MZ1500 0x06 // Video mode = MZ1500
#define VMMODE_MZ80B 0x07 // Video mode = MZ80B
#define VMMODE_MZ2000 0x08 // Video mode = MZ2000
#define VMMODE_MZ2200 0x09 // Video mode = MZ2200
#define VMMODE_MZ2500 0x0A // Video mode = MZ2500
#define VMMODE_80CHAR 0x80 // Enable 80 character display.
#define VMMODE_80CHAR_MASK 0x7F // Mask to filter out display width control bit.
#define VMMODE_COLOUR 0x20 // Enable colour display.
#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit.
// Sharp MZ colour attributes.
#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute.
#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute.
#define VMATTR_FG_RED 0x20 // Foreground red character attribute.
#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute.
#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute.
#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute.
#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute.
#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute.
#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute.
#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute.
#define VMATTR_BG_BLACK 0x00 // Background black character attribute.
#define VMATTR_BG_BLUE 0x01 // Background blue character attribute.
#define VMATTR_BG_RED 0x02 // Background red character attribute.
#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute.
#define VMATTR_BG_GREEN 0x04 // Background green character attribute.
#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute.
#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute.
#define VMATTR_BG_WHITE 0x07 // Background white character attribute.
#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute.
#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute.
#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute.
#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute.
#define VMATTR_FG_RED 0x20 // Foreground red character attribute.
#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute.
#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute.
#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute.
#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute.
#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute.
#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute.
#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute.
#define VMATTR_BG_BLACK 0x00 // Background black character attribute.
#define VMATTR_BG_BLUE 0x01 // Background blue character attribute.
#define VMATTR_BG_RED 0x02 // Background red character attribute.
#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute.
#define VMATTR_BG_GREEN 0x04 // Background green character attribute.
#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute.
#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute.
#define VMATTR_BG_WHITE 0x07 // Background white character attribute.
#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute.
#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute.
// Sharp MZ constants.
//
#define MBADDR_KEYPA 0xE000 // Mainboard 8255 Port A
#define MBADDR_KEYPB 0xE001 // Mainboard 8255 Port B
#define MBADDR_KEYPC 0xE002 // Mainboard 8255 Port C
#define MBADDR_KEYPF 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CSTR 0xE002 // Mainboard 8255 Port C
#define MBADDR_CSTPT 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CONT0 0xE004 // Mainboard 8253 Counter 0
#define MBADDR_CONT1 0xE005 // Mainboard 8253 Counter 1
#define MBADDR_CONT2 0xE006 // Mainboard 8253 Counter 1
#define MBADDR_CONTF 0xE007 // Mainboard 8253 Mode Control
#define MBADDR_SUNDG 0xE008 // Register for reading the tempo timer status (cursor flash). horizontal blank and switching sound on/off.
#define MBADDR_TEMP 0xE008 // As above, different name used in original source when writing.
#define MBADDR_MEMSW 0xE00C // Memory swap, 0000->C000, C000->0000
#define MBADDR_MEMSWR 0xE010 // Reset memory swap.
#define MBADDR_NRMDSP 0xE014 // Return display to normal.
#define MBADDR_INVDSP 0xE015 // Invert display.
#define MBADDR_SCLDSP 0xE200 // Hardware scroll, a read to each location adds 8 to the start of the video access address therefore creating hardware scroll. 00 - reset to power up
#define MBADDR_SCLBASE 0xE2 // High byte scroll base.
#define MBADDR_DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
#define MBADDR_KEYPA 0xE000 // Mainboard 8255 Port A
#define MBADDR_KEYPB 0xE001 // Mainboard 8255 Port B
#define MBADDR_KEYPC 0xE002 // Mainboard 8255 Port C
#define MBADDR_KEYPF 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CSTR 0xE002 // Mainboard 8255 Port C
#define MBADDR_CSTPT 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CONT0 0xE004 // Mainboard 8253 Counter 0
#define MBADDR_CONT1 0xE005 // Mainboard 8253 Counter 1
#define MBADDR_CONT2 0xE006 // Mainboard 8253 Counter 1
#define MBADDR_CONTF 0xE007 // Mainboard 8253 Mode Control
#define MBADDR_SUNDG 0xE008 // Register for reading the tempo timer status (cursor flash). horizontal blank and switching sound on/off.
#define MBADDR_TEMP 0xE008 // As above, different name used in original source when writing.
#define MBADDR_MEMSW 0xE00C // Memory swap, 0000->C000, C000->0000
#define MBADDR_MEMSWR 0xE010 // Reset memory swap.
#define MBADDR_NRMDSP 0xE014 // Return display to normal.
#define MBADDR_INVDSP 0xE015 // Invert display.
#define MBADDR_SCLDSP 0xE200 // Hardware scroll, a read to each location adds 8 to the start of the video access address therefore creating hardware scroll. 00 - reset to power up
#define MBADDR_SCLBASE 0xE2 // High byte scroll base.
#define MBADDR_DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
//Common character definitions.
#define SCROLL 0x01 // Set scroll direction UP.
#define BELL 0x07
#define ENQ 0x05
#define SPACE 0x20
#define TAB 0x09 // TAB ACROSS (8 SPACES FOR SD-BOARD)
#define CR 0x0D
#define LF 0x0A
#define FF 0x0C
#define DELETE 0x7F
#define BACKS 0x08
#define SOH 0x01 // For XModem etc.
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define NUL 0x00
//#define NULL 0x00
#define CTRL_A 0x01
#define CTRL_B 0x02
#define CTRL_C 0x03
#define CTRL_D 0x04
#define CTRL_E 0x05
#define CTRL_F 0x06
#define CTRL_G 0x07
#define CTRL_H 0x08
#define CTRL_I 0x09
#define CTRL_J 0x0A
#define CTRL_K 0x0B
#define CTRL_L 0x0C
#define CTRL_M 0x0D
#define CTRL_N 0x0E
#define CTRL_O 0x0F
#define CTRL_P 0x10
#define CTRL_Q 0x11
#define CTRL_R 0x12
#define CTRL_S 0x13
#define CTRL_T 0x14
#define CTRL_U 0x15
#define CTRL_V 0x16
#define CTRL_W 0x17
#define CTRL_X 0x18
#define CTRL_Y 0x19
#define CTRL_Z 0x1A
#define ESC 0x1B
#define CTRL_SLASH 0x1C
#define CTRL_LB 0x1B
#define CTRL_RB 0x1D
#define CTRL_CAPPA 0x1E
#define CTRL_UNDSCR 0x1F
#define CTRL_AT 0x00
#define FUNC1 0x80
#define FUNC2 0x81
#define FUNC3 0x82
#define FUNC4 0x83
#define FUNC5 0x84
#define FUNC6 0x85
#define FUNC7 0x86
#define FUNC8 0x87
#define FUNC9 0x88
#define FUNC10 0x89
#define PAGEUP 0xE0
#define PAGEDOWN 0xE1
#define CURHOMEKEY 0xE2
#define ALPHAGRAPHKEY 0xE3
#define HOTKEY_ORIGINAL 0xE8
#define HOTKEY_RFS80 0xE9
#define HOTKEY_RFS40 0xEA
#define HOTKEY_TZFS 0xEB
#define HOTKEY_LINUX 0xEC
#define NOKEY 0xF0
#define CURSRIGHT 0xF1
#define CURSLEFT 0xF2
#define CURSUP 0xF3
#define CURSDOWN 0xF4
#define DBLZERO 0xF5
#define INSERT 0xF6
#define CLRKEY 0xF7
#define HOMEKEY 0xF8
#define ENDKEY 0xF9
#define ANSITGLKEY 0xFA
#define BREAKKEY 0xFB
#define GRAPHKEY 0xFC
#define ALPHAKEY 0xFD
#define DEBUGKEY 0xFE // Special key to enable debug features such as the ANSI emulation.
#define SCROLL 0x01 // Set scroll direction UP.
#define BELL 0x07
#define ENQ 0x05
#define SPACE 0x20
#define TAB 0x09 // TAB ACROSS (8 SPACES FOR SD-BOARD)
#define CR 0x0D
#define LF 0x0A
#define FF 0x0C
#define DELETE 0x7F
#define BACKS 0x08
#define SOH 0x01 // For XModem etc.
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define NUL 0x00
//#define NULL 0x00
#define CTRL_A 0x01
#define CTRL_B 0x02
#define CTRL_C 0x03
#define CTRL_D 0x04
#define CTRL_E 0x05
#define CTRL_F 0x06
#define CTRL_G 0x07
#define CTRL_H 0x08
#define CTRL_I 0x09
#define CTRL_J 0x0A
#define CTRL_K 0x0B
#define CTRL_L 0x0C
#define CTRL_M 0x0D
#define CTRL_N 0x0E
#define CTRL_O 0x0F
#define CTRL_P 0x10
#define CTRL_Q 0x11
#define CTRL_R 0x12
#define CTRL_S 0x13
#define CTRL_T 0x14
#define CTRL_U 0x15
#define CTRL_V 0x16
#define CTRL_W 0x17
#define CTRL_X 0x18
#define CTRL_Y 0x19
#define CTRL_Z 0x1A
#define ESC 0x1B
#define CTRL_SLASH 0x1C
#define CTRL_LB 0x1B
#define CTRL_RB 0x1D
#define CTRL_CAPPA 0x1E
#define CTRL_UNDSCR 0x1F
#define CTRL_AT 0x00
#define FUNC1 0x80
#define FUNC2 0x81
#define FUNC3 0x82
#define FUNC4 0x83
#define FUNC5 0x84
#define FUNC6 0x85
#define FUNC7 0x86
#define FUNC8 0x87
#define FUNC9 0x88
#define FUNC10 0x89
#define PAGEUP 0xE0
#define PAGEDOWN 0xE1
#define CURHOMEKEY 0xE2
#define ALPHAGRAPHKEY 0xE3
#define HOTKEY_ORIGINAL 0xE8
#define HOTKEY_RFS80 0xE9
#define HOTKEY_RFS40 0xEA
#define HOTKEY_TZFS 0xEB
#define HOTKEY_LINUX 0xEC
#define NOKEY 0xF0
#define CURSRIGHT 0xF1
#define CURSLEFT 0xF2
#define CURSUP 0xF3
#define CURSDOWN 0xF4
#define DBLZERO 0xF5
#define INSERT 0xF6
#define CLRKEY 0xF7
#define HOMEKEY 0xF8
#define ENDKEY 0xF9
#define ANSITGLKEY 0xFA
#define BREAKKEY 0xFB
#define GRAPHKEY 0xFC
#define ALPHAKEY 0xFD
#define DEBUGKEY 0xFE // Special key to enable debug features such as the ANSI emulation.
// Macros.
//
@@ -285,7 +313,7 @@ typedef struct {
uint8_t dispCode;
} t_dispCodeMap;
// Mapping table from keyboard scan codes to Sharp MZ-700 keys.
// Mapping table from keyboard scan codes to Sharp MZ keys.
//
typedef struct {
uint8_t scanCode[80];

View File

@@ -431,7 +431,7 @@ uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#if defined(INCLUDE_TEST_METHODS) && INCLUDE_TEST_METHODS == 1
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)

View File

@@ -39,7 +39,7 @@
#endif
// Definitions to control compilation.
//#define INCLUDE_TEST_METHODS 0
#define INCLUDE_TEST_METHODS 0
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
@@ -82,6 +82,14 @@
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_READIO_WRITE_ADDR 0x38
#define CPLD_CMD_READIO_WRITE_ADDR_P1 0x39
#define CPLD_CMD_READIO_WRITE_ADDR_P2 0x3A
#define CPLD_CMD_READIO_WRITE_ADDR_P3 0x3B
#define CPLD_CMD_READIO_WRITE_ADDR_P4 0x3C
#define CPLD_CMD_READIO_WRITE_ADDR_P5 0x3D
#define CPLD_CMD_READIO_WRITE_ADDR_P6 0x3E
#define CPLD_CMD_READIO_WRITE_ADDR_P7 0x3F
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
@@ -386,8 +394,8 @@
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4)
#define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
#define SPI_SEND_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
@@ -396,9 +404,18 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND_I_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -406,10 +423,9 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
#define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -417,24 +433,63 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32i(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
pr_info("Stage 0");\
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
pr_info("Stage 1");\
#define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) != 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
pr_info("Stage 2");\
timeout = MAX_CHECK_CNT; \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \
pr_info("Stage 3");\
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SET_FRAME_SIZE() { MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_I_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND_48(_d1_, _d2_, _d3_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 6); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d3_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+2, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte

View File

@@ -1,794 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80ctrl.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Control Interface
// This file contains a command line utility tool for controlling the z80drv device
// driver. The tool allows manipulation of the emulated Z80, inspection of its
// memory and data, transmission of adhoc commands to the underlying CPLD-Z80
// gateway and loading/saving of programs and data to/from the Z80 virtual and
// host memory.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of the Z80 device driver.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <Z/constants/pointer.h>
#include <Z/macros/member.h>
#include <Z/macros/array.h>
#include <Z80.h>
#include "z80driver.h"
#define VERSION "1.0"
#define AUTHOR "P.D.Smart"
#define COPYRIGHT "(c) 2018-22"
// Getopt_long is buggy so we use optparse.
#define OPTPARSE_IMPLEMENTATION
#define OPTPARSE_API static
#include "optparse.h"
// Device driver name.
#define DEVICE_FILENAME "/dev/z80drv"
// Constants for the Sharp MZ80A MZF file format.
#define MZF_HEADER_SIZE 128 // Size of the MZF header.
#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code.
#define MZF_FILENAME 0x01 // Title/Name (17 bytes).
#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed.
#define MZF_FILESIZE 0x12 // Size of program.
#define MZF_LOADADDR 0x14 // Load address of program.
#define MZF_EXECADDR 0x16 // Exec address of program.
#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code.
#define MZF_COMMENT_LEN 104 // Length of the comment field.
#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object.
#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program.
#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program.
#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0.
#define CMT_TYPE_TZOBJCD1 0x0F9
#define CMT_TYPE_TZOBJCD2 0x0FA
#define CMT_TYPE_TZOBJCD3 0x0FB
#define CMT_TYPE_TZOBJCD4 0x0FC
#define CMT_TYPE_TZOBJCD5 0x0FD
#define CMT_TYPE_TZOBJCD6 0x0FE
#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7.
#define MZ_CMT_ADDR 0x10F0
// Structure to define a Sharp MZ80A MZF directory structure. This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images.
//
typedef struct __attribute__((__packed__)) {
uint8_t attr; // MZF attribute describing the file.
uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename.
uint16_t fileSize; // Size of file.
uint16_t loadAddr; // Load address for the file.
uint16_t execAddr; // Execution address where the Z80 starts processing.
uint8_t comment[MZF_COMMENT_LEN]; // Text comment field but often contains a startup machine code program.
} t_svcDirEnt;
// Possible commands to be issued to the Z80 driver.
enum CTRL_COMMANDS {
Z80_CMD_STOP = 0,
Z80_CMD_START = 1,
Z80_CMD_PAUSE = 2,
Z80_CMD_CONTINUE = 3,
Z80_CMD_RESET = 4,
Z80_CMD_SPEED = 5,
Z80_CMD_HOST_RAM = 6,
Z80_CMD_VIRTUAL_RAM = 7,
Z80_CMD_DUMP_MEMORY = 8,
Z80_CMD_MEMORY_TEST = 9,
CPLD_CMD_SEND_CMD = 10,
CPLD_CMD_SPI_TEST = 11,
CPLD_CMD_PRL_TEST = 12
};
// Shared memory between this process and the Z80 driver.
static t_Z80Ctrl *Z80Ctrl = NULL;
// Method to obtain and return the output screen width.
//
uint8_t getScreenWidth(void)
{
return(MAX_SCREEN_WIDTH);
}
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch(uint8_t wait)
{
int r;
unsigned char c;
if(wait != 0 || (wait == 0 && kbhit()))
{
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
return 0;
}
void delay(int number_of_seconds)
{
// Converting time into milli_seconds
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
}
// Function to dump out a given section of memory via the UART.
//
int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryFlag, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth)
{
uint8_t displayWidth = dispwidth;;
uint32_t pnt = memaddr;
uint32_t endAddr = memaddr + memsize;
uint32_t addr = dispaddr;
uint32_t i = 0;
//uint32_t data;
int8_t keyIn;
int result = -1;
char c = 0;
// Sanity check. memoryFlag == 0 required kernel driver to dump so we exit as it cannot be performed here.
if(memoryFlag == 0)
return(-1);
// Reconfigure terminal to allow non-blocking key input.
//
set_conio_terminal_mode();
// If not set, calculate output line width according to connected display width.
//
if(displayWidth == 0)
{
switch(getScreenWidth())
{
case 40:
displayWidth = 8;
break;
case 80:
displayWidth = 16;
break;
default:
displayWidth = 32;
break;
}
}
while (1)
{
printf("%08lX", addr); // print address
printf(": ");
// print hexadecimal data
for (i=0; i < displayWidth; )
{
switch(memwidth)
{
case 16:
if(pnt+i < endAddr)
printf("%04X", memoryFlag == 1 ? (uint16_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint16_t)Z80Ctrl->page[pnt+i] : (uint16_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 32:
if(pnt+i < endAddr)
printf("%08lX", memoryFlag == 1 ? (uint32_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint32_t)Z80Ctrl->page[pnt+i] : (uint32_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 8:
default:
if(pnt+i < endAddr)
printf("%02X", memoryFlag == 1 ? (uint8_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint8_t)Z80Ctrl->page[pnt+i] : (uint8_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
}
fputc((char)' ', stdout);
}
// print ascii data
printf(" |");
// print single ascii char
for (i=0; i < displayWidth; i++)
{
c = memoryFlag == 1 ? (char)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (char)Z80Ctrl->page[pnt+i] : (char)Z80Ctrl->iopage[pnt+i];
if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~'))
fputc((char)c, stdout);
else
fputc((char)' ', stdout);
}
printf("|\r\n");
fflush(stdout);
// Move on one row.
pnt += displayWidth;
addr += displayWidth;
// User abort (ESC), pause (Space) or all done?
//
keyIn = getch(0);
if(keyIn == ' ')
{
do {
keyIn = getch(0);
} while(keyIn != ' ' && keyIn != 0x1b);
}
// Escape key pressed, exit with 0 to indicate this to caller.
if (keyIn == 0x1b)
{
sleep(1);
result = 0;
goto memoryDumpExit;
}
// End of buffer, exit the loop.
if(pnt >= (memaddr + memsize))
{
break;
}
}
// Normal exit, return -1 to show no key pressed.
memoryDumpExit:
reset_terminal_mode();
return(result);
}
// Method to load a program or data file into the Z80 memory. First load into Virtual memory and then trigger a sync to bring Host RAM in line.
//
int z80load(int fdZ80, char *fileName)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
t_svcDirEnt mzfHeader;
// Pause the Z80.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Open the file and read directly into the Virtual memory via the share.
FILE *ptr;
ptr = fopen(fileName, "rb");
if(ptr)
{
// First the header.
fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr);
#if(TARGET_HOST_MZ700 == 1)
if(mzfHeader.loadAddr > 0x1000)
{
#endif
// Copy in the header.
memcpy((uint8_t *)&Z80Ctrl->memory[MZ_CMT_ADDR], (uint8_t *)&mzfHeader, MZF_HEADER_SIZE);
// Now read in the data.
fread(&Z80Ctrl->memory[mzfHeader.loadAddr], mzfHeader.fileSize, 1, ptr);
printf("Loaded %s, Size:%04x, Addr:%04x, Exec:%04x\n", fileName, mzfHeader.fileSize, mzfHeader.loadAddr, mzfHeader.execAddr);
#if(TARGET_HOST_MZ700 == 1)
}
#endif
// Sync the loaded image from Virtual memory to hard memory.
ioctlCmd.cmd = IOCTL_CMD_SYNC_TO_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#if(TARGET_HOST_MZ2000 == 1)
// Set PC to 2 (NST) which switches to RUN mode and executes at 0000H
ioctlCmd.z80.pc = 2;
#endif
#if(TARGET_HOST_MZ700 == 1)
// MZ-700 just use the MZF header exec address.
ioctlCmd.z80.pc = mzfHeader.execAddr;
#endif
// Set PC to required setting ready for run.
ioctlCmd.cmd = IOCTL_CMD_SETPC;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Resume Z80 processing.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
else
printf("Couldnt open file\n");
return ret;
}
// Method to request basic Z80 operations.
//
int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long param3)
{
// Locals.
struct ioctlCmd ioctlCmd;
uint32_t idx;
int ret = 0;
switch(cmd)
{
case Z80_CMD_STOP:
// Use IOCTL to request Z80 to Stop (power off) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_START:
// Use IOCTL to request Z80 to Start (power on) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_PAUSE:
// Use IOCTL to request Z80 to pause processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_CONTINUE:
// Use IOCTL to request Z80 continue processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_RESET:
// Use IOCTL to request Z80 reset.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_SPEED:
// Check value is in range.
for(idx=1; idx < 256; idx+=idx)
{
if((uint32_t)param1 == idx) break;
}
if(idx == 256)
{
printf("Speed factor is illegal. It must be a multiple value of the original CPU clock, ie. 1x, 2x, 4x etc\n");
ret = -1;
} else
{
// Use IOCTL to request Z80 cpu freq change.
ioctlCmd.speed.speedMultiplier = (uint32_t)param1;
ioctlCmd.cmd = IOCTL_CMD_Z80_CPU_FREQ;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case CPLD_CMD_SEND_CMD:
// Build up the IOCTL command to request the given data is sent to the CPLD.
ioctlCmd.cmd = IOCTL_CMD_CPLD_CMD;
ioctlCmd.cpld.cmd = (uint32_t)param1;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_DUMP_MEMORY:
// If virtual memory, we can dump it via the shared memory segment.
if((uint8_t)param1)
{
memoryDump((uint32_t)param2, (uint32_t)param3, (uint8_t)param1, (uint8_t)param1 == 2 || (uint8_t)param1 == 3 ? 32 : 8, (uint32_t)param2, 0);
} else
{
// Build an IOCTL command to get the driver to dump the memory.
ioctlCmd.cmd = IOCTL_CMD_DUMP_MEMORY;
ioctlCmd.addr.start = (uint32_t)param2;
ioctlCmd.addr.end = (uint32_t)param2+(uint32_t)param3;
ioctlCmd.addr.size = (uint32_t)param3;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case Z80_CMD_HOST_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_VIRTUAL_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_VIRTUAL_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_MEMORY_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_Z80_MEMTEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_PRL_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_PRL_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_SPI_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_SPI_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
default:
printf("Command not supported!\n");
ret = -1;
break;
}
return ret;
}
// Method to perform some simple tests on the Z80 emulator.
//
int z80test(int fdZ80)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
// Stop the Z80.
//
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
FILE *ptr;
ptr = fopen("/customer/mz700.rom", "rb");
if(ptr)
{
fread(&Z80Ctrl->memory, 65536, 1, ptr);
} else printf("Couldnt open file\n");
// Configure the Z80.
//
printf("Send SETPC\n");
ioctlCmd.z80.pc = 0;
ioctl(fdZ80, IOCTL_CMD_SETPC, &ioctlCmd);
memoryDump(0 , 65536, 1, 8, 0, 0);
// Start the Z80.
//
printf("Send START\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(10);
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
memoryDump(0, 65536, 1, 8, 0, 0);
out:
return ret;
}
// Output usage screen. So mamy commands you do need to be prompted!!
void showArgs(char *progName, struct optparse *options)
{
printf("%s %s %s %s\n\n", progName, VERSION, COPYRIGHT, AUTHOR);
printf("Synopsis:\n");
printf("%s --help # This help screen.\n", progName);
printf(" --cmd <command> = RESET # Reset the Z80\n");
printf(" = STOP # Stop and power off the Z80\n");
printf(" = START # Power on and start the Z80\n");
printf(" = PAUSE # Pause running Z80\n");
printf(" = CONTINUE # Continue Z80 execution\n");
printf(" = HOSTRAM # Use HOST DRAM\n");
printf(" = VIRTRAM # Use Virtual RAM\n");
printf(" = SPEED --speed <1, 2, 4, 8, 16, 32, 64, 128> # In Virtual RAM mode, set CPU speed to base clock x factor.\n");
printf(" = LOADMZF --file <mzf filename> # Load MZF file into memory.\n");
printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>]--virtual <0 - Host RAM, 1 = Virtual RAM, 2 = PageTable, 3 = IOPageTable>\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
printf(" = Z80TEST # Perform various debugging tests\n");
printf(" = SPITEST # Perform SPI testing\n");
printf(" = PRLTEST # Perform Parallel Bus testing\n");
printf(" = Z80MEMTEST # Perform HOST memory tests.\n");
printf(" --<cmd> # Some commands can be abbreviated.\n");
}
int main(int argc, char *argv[])
{
int fdZ80;
char buff[64];
char cmd[64] = { 0 };
char fileName[256] = { 0 };
int opt;
uint32_t hexData = 0;
long speedMultiplier = 1;
long startAddr = 0x0000;
long endAddr = 0x1000;
int virtualMemory = 0;
int helpFlag = 0;
int verboseFlag = 0;
// Define parameters to be processed.
struct optparse options;
static struct optparse_long long_options[] =
{
{"help", 'h', OPTPARSE_NONE},
{"cmd", 'c', OPTPARSE_REQUIRED},
{"file", 'f', OPTPARSE_REQUIRED},
{"data", 'd', OPTPARSE_REQUIRED},
{"speed", 'S', OPTPARSE_REQUIRED},
{"virtual", 'V', OPTPARSE_REQUIRED},
{"addr", 'a', OPTPARSE_REQUIRED},
{"end", 'e', OPTPARSE_REQUIRED},
{"size", 's', OPTPARSE_REQUIRED},
{"verbose", 'v', OPTPARSE_NONE},
{"dump", '1', OPTPARSE_NONE},
{"loadmzf", '2', OPTPARSE_NONE},
{"reset", '3', OPTPARSE_NONE},
{"stop", '4', OPTPARSE_NONE},
{"start", '5', OPTPARSE_NONE},
{"pause", '6', OPTPARSE_NONE},
{"continue", '7', OPTPARSE_NONE},
{"speed", '8', OPTPARSE_NONE},
{"cpldcmd", '9', OPTPARSE_NONE},
{0}
};
// Parse the command line options.
//
optparse_init(&options, argv);
while((opt = optparse_long(&options, long_options, NULL)) != -1)
{
switch(opt)
{
// Hex data.
case 'd':
// hexData = (uint32_t)strtol(options.optarg, NULL, 0);
sscanf(options.optarg, "0x%08x", &hexData);
printf("Hex data:%08x\n", hexData);
break;
// Start address for memory operations.
case 'a':
startAddr = strtol(options.optarg, NULL, 0);
//printf("Start Addr:%04x\n", startAddr);
break;
// Speed multiplication factor for CPU governor when running in virtual memory.
case 'S':
speedMultiplier = strtol(options.optarg, NULL, 0);
//printf("Speed = base freq x %d\n", speedFactor);
break;
// End address for memory operations.
case 'e':
endAddr = strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Size instead of end address for memory operations.
case 's':
endAddr = startAddr + strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Virtual memory flag, 0 = host, 1 = virtual memory, 2 = page table, 3 = iopage table.
case 'V':
virtualMemory = atoi(options.optarg);
break;
// Filename.
case 'f':
strcpy(fileName, options.optarg);
break;
// Command to execute.
case 'c':
strcpy(cmd, options.optarg);
break;
// Quick command flags.
case '1':
strcpy(cmd, "DUMP");
break;
case '2':
strcpy(cmd, "LOADMZF");
break;
case '3':
strcpy(cmd, "RESET");
break;
case '4':
strcpy(cmd, "STOP");
break;
case '5':
strcpy(cmd, "START");
break;
case '6':
strcpy(cmd, "PAUSE");
break;
case '7':
strcpy(cmd, "CONTINUE");
break;
case '8':
strcpy(cmd, "SPEED");
break;
case '9':
strcpy(cmd, "CPLDCMD");
break;
// Verbose mode.
case 'v':
verboseFlag = 1;
break;
// Command help needed.
case 'h':
helpFlag = 1;
showArgs(argv[0], &options);
break;
// Unrecognised, show synopsis.
case '?':
showArgs(argv[0], &options);
printf("%s: %s\n", argv[0], options.errmsg);
return(1);
}
}
// Open the z80drv driver and attach to its shared memory, basically the Z80 control structure which includes the virtual Z80 memory.
fdZ80 = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
if(fdZ80 >= 0)
{
Z80Ctrl = (t_Z80Ctrl *)mmap(0, sizeof(t_Z80Ctrl), PROT_READ | PROT_WRITE, MAP_SHARED, fdZ80, 0);
if(Z80Ctrl == (void *)-1)
{
printf("Failed to attach to the Z80 Control structure, cannot continue, exitting....\n");
close(fdZ80);
exit(1);
}
} else
{
printf("Failed to open the Z80 Driver, exitting...\n");
exit(1);
}
// Basic string to method mapping. Started off with just 1 or two but has grown, may need a table!
if(strcasecmp(cmd, "LOADMZF") == 0)
{
z80load(fdZ80, fileName);
} else
if(strcasecmp(cmd, "RESET") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_RESET, 0, 0, 0);
} else
if(strcasecmp(cmd, "STOP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_STOP, 0, 0, 0);
} else
if(strcasecmp(cmd, "START") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_START, 0, 0, 0);
} else
if(strcasecmp(cmd, "PAUSE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_PAUSE, 0, 0, 0);
} else
if(strcasecmp(cmd, "CONTINUE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_CONTINUE, 0, 0, 0);
} else
if(strcasecmp(cmd, "SPEED") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_SPEED, speedMultiplier, 0, 0);
} else
if(strcasecmp(cmd, "DUMP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_DUMP_MEMORY, virtualMemory, startAddr, (endAddr - startAddr));
} else
if(strcasecmp(cmd, "HOSTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_HOST_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "VIRTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_VIRTUAL_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "CPLDCMD") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SEND_CMD, hexData, 0, 0);
} else
// Test methods, if the code is built-in to the driver.
if(strcasecmp(cmd, "Z80TEST") == 0)
{
z80test(fdZ80);
} else
if(strcasecmp(cmd, "SPITEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SPI_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "PRLTEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_PRL_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "Z80MEMTEST") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_MEMORY_TEST, 0, 0, 0);
}
else
{
showArgs(argv[0], &options);
printf("No command given, nothing done!\n");
}
// Unmap shared memory and close the device.
munmap(Z80Ctrl, sizeof(t_Z80Ctrl));
close(fdZ80);
return(0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,326 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80driver.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Driver
// This file contains the declarations used in the z80drv device driver.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of this driver and in all ways, is compatible with
// the original Z80.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80DRIVER_H
#define Z80DRIVER_H
// Constants.
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ2000 1
#define Z80_VIRTUAL_ROM_SIZE 16384 // Sized to maximum ROM which is the MZ-800 ROM.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 8) // (PAGE_SIZE * 2) // max size mmaped to userspace
#define Z80_VIRTUAL_MEMORY_SIZE Z80_VIRTUAL_RAM_SIZE + Z80_VIRTUAL_ROM_SIZE
#define Z80_MEMORY_PAGE_SIZE 16
#define MAX_SCREEN_WIDTH 132
#define DEVICE_NAME "z80drv"
#define CLASS_NAME "mogu"
// Memory and IO page types. Used to create a memory page which maps type of address space to real address space on host or virtual memory.
#define MEMORY_TYPE_VIRTUAL_MASK 0x00FFFFFF
#define MEMORY_TYPE_REAL_MASK 0x0000FFFF
#define IO_TYPE_MASK 0x0000FFFF
#define MEMORY_TYPE_INHIBIT 0x00000000
#define MEMORY_TYPE_PHYSICAL_RAM 0x80000000
#define MEMORY_TYPE_PHYSICAL_ROM 0x40000000
#define MEMORY_TYPE_PHYSICAL_VRAM 0x20000000
#define MEMORY_TYPE_PHYSICAL_HW 0x10000000
#define MEMORY_TYPE_VIRTUAL_RAM 0x08000000
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_HW 0x02000000
#define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000
// Approximate governor delays to regulate emulated CPU speed.
// MZ-700
#if(TARGET_HOST_MZ700 == 1)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
// MZ-2000
#if(TARGET_HOST_MZ2000 == 1)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 243
#define INSTRUCTION_DELAY_ROM_7MHZ 122
#define INSTRUCTION_DELAY_ROM_14MHZ 61
#define INSTRUCTION_DELAY_ROM_28MHZ 30
#define INSTRUCTION_DELAY_ROM_56MHZ 15
#define INSTRUCTION_DELAY_ROM_112MHZ 7
#define INSTRUCTION_DELAY_ROM_224MHZ 3
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 218
#define INSTRUCTION_DELAY_RAM_7MHZ 112
#define INSTRUCTION_DELAY_RAM_14MHZ 56
#define INSTRUCTION_DELAY_RAM_28MHZ 28
#define INSTRUCTION_DELAY_RAM_56MHZ 14
#define INSTRUCTION_DELAY_RAM_112MHZ 7
#define INSTRUCTION_DELAY_RAM_224MHZ 3
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S'
#define IOCTL_CMD_Z80_PAUSE 'P'
#define IOCTL_CMD_Z80_RESET 'R'
#define IOCTL_CMD_Z80_CONTINUE 'C'
#define IOCTL_CMD_USE_HOST_RAM 'x'
#define IOCTL_CMD_USE_VIRTUAL_RAM 'X'
#define IOCTL_CMD_DUMP_MEMORY 'M'
#define IOCTL_CMD_Z80_CPU_FREQ 'F'
#define IOCTL_CMD_CPLD_CMD 'z'
#define IOCTL_CMD_SEND _IOW('c', 'c', int32_t *)
#define IOCTL_CMD_SETPC _IOW('p', 'p', int32_t *)
#define IOCTL_CMD_SYNC_TO_HOST_RAM 'V'
#define IOCTL_CMD_SPI_TEST '1'
#define IOCTL_CMD_PRL_TEST '2'
#define IOCTL_CMD_Z80_MEMTEST '3'
// Chip Select map MZ80K-MZ700.
//
// 0000 - 0FFF = CS_ROMni : R/W : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF = CS_RAMni : R/W : MZ80K/A/700 = RAM
// C000 - CFFF = CS_ROMni : R/W : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF = CS_VRAMni : R/W : MZ80K/A/700 = VRAM
// D800 - DFFF = CS_VRAMni : R/W : MZ700 = Colour VRAM (MZ700)
// E000 - E003 = CS_8255n : R/W : MZ80K/A/700 = 8255
// E004 - E007 = CS_8254n : R/W : MZ80K/A/700 = 8254
// E008 - E00B = CS_LS367n : R/W : MZ80K/A/700 = LS367
// E00C - E00F = CS_ESWPn : R : MZ80A = Memory Swap (MZ80A)
// E010 - E013 = CS_ESWPn : R : MZ80A = Reset Memory Swap (MZ80A)
// E014 = CS_E5n : R/W : MZ80A/700 = Normal CRT display (in Video Controller)
// E015 = CS_E6n : R/W : MZ80A/700 = Reverse CRT display (in Video Controller)
// E200 - E2FF = : R/W : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF = : R/W : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF = : R/W : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF = : R/W : MZ80K/A/700 = Floppy Disk interface.
//
// Chip Select map MZ800
//
// FC - FF = CS_PIOn : R/W : MZ800/MZ1500 = Z80 PIO Printer Interface
// F2 = CS_PSG0n : W : MZ800/MZ1500 = Programable Sound Generator, MZ-800 = Mono, MZ-1500 = Left Channel
// F3 = CS_PSG1n : W : MZ1500 = Programable Sound Generator, MZ-1500 = Right Channel
// E9 = CS_PSG(X)n: W : MZ1500 = Simultaneous write to both PSG's.
// F0 - F1 = CS_JOYSTK : R : MZ800 = Joystick 1 and 2
// CC = CS_GWF : W : MZ800 = CRTC GWF Write format Register
// CD = CS_GRF : W : MZ800 = CRTC GRF Read format Register
// CE = CS_GDMD : W : MZ800 = CRTC GDMD Mode Register
// CF = CS_GCRTC : W : MZ800 = CRTC GCRTC Control Register
// D4 - D7 = CS
// D000 - DFFF
// MZ700/MZ800 memory mode switch?
//
// MZ-700 MZ-800
// |0000:0FFF|1000:1FFF|1000:CFFF|C000:CFFF|D000:FFFF |0000:7FFF|1000:1FFF|2000:7FFF|8000:BFFF|C000:CFFF|C000:DFFF|E000:FFFF
// -------------------------------------------------- ----------------------------------------------------------------------
// OUT 0xE0 = |DRAM | | | | |DRAM | | | | | |
// OUT 0xE1 = | | | | |DRAM | | | | | | |DRAM
// OUT 0xE2 = |MONITOR | | | | |MONITOR | | | | | |
// OUT 0xE3 = | | | | |Memory Mapped I/O | | | | | | |Upper MONITOR ROM
// OUT 0xE4 = |MONITOR | |DRAM | |Memory Mapped I/O |MONITOR |CGROM |DRAM |VRAM | |DRAM |Upper MONITOR ROM
// OUT 0xE5 = | | | | |Inhibit | | | | | | |Inhibit
// OUT 0xE6 = | | | | |<return> | | | | | | |<return>
// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | |
// IN 0xE1 = | |DRAM | |DRAM | | |<return> | |DRAM | | |
//
// <return> = Return to the state prior to the complimentary command being invoked.
// * = MZ-800 host only.
// Macros to lookup and test to see if a given memory block or IO byte is of a given type. Also macros to read/write to the memory block and IO byte.
#define MEMORY_BLOCK_GRANULARITY 0x800
#define MEMORY_BLOCK_SLOTS (0x10000 / MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_MASK (0x10000 - MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_SHIFT 11
#define getPageData(a) (Z80Ctrl->page[(a & 0xF800) >> MEMORY_BLOCK_SHIFT])
#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF])
#define getPageType(a, mask) (getPageData(a) & mask)
#define getPageAddr(a, mask) ((getPageData(a) & mask) + (a & (MEMORY_BLOCK_GRANULARITY-1)))
#define getIOPageType(a, mask) (getIOPageData(a) & mask)
#define getIOPageAddr(a, mask) (getIOPageData(a) & mask)
#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)])
#define realPort(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_MASK)
#define isPhysicalRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_RAM))
#define isPhysicalVRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_VRAM))
#define isPhysicalROM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_ROM))
#define isPhysicalMemory(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))])
#define isPhysicalHW(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_HW))
#define isPhysical(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM)))
#define isPhysicalIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_PHYSICAL_HW)
#define isVirtualRAM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM))
#define isVirtualROM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_ROM))
#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM)))
#define isVirtualHW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_HW))
#define isVirtualIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_VIRTUAL_HW)
#define isHW(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_VIRTUAL_HW)))
#define readVirtualRAM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ])
#define readVirtualROM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE ])
#define writeVirtualRAM(a, d) { Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; }
#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; }
#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = Z80Ctrl->page[_block_]; }
#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; }
#define IO_ADDR_E0 0xE0
#define IO_ADDR_E1 0xE1
#define IO_ADDR_E2 0xE2
#define IO_ADDR_E3 0xE3
#define IO_ADDR_E4 0xE4
#define IO_ADDR_E5 0xE5
#define IO_ADDR_E6 0xE6
#define IO_ADDR_E7 0xE7
#define IO_ADDR_E8 0xE8
#define IO_ADDR_E9 0xE9
#define IO_ADDR_EA 0xEA
#define IO_ADDR_EB 0xEB
enum Z80_RUN_STATES {
Z80_STOP = 0x00,
Z80_STOPPED = 0x01,
Z80_PAUSE = 0x02,
Z80_PAUSED = 0x03,
Z80_CONTINUE = 0x04,
Z80_RUNNING = 0x05,
};
enum Z80_MEMORY_PROFILE {
USE_PHYSICAL_RAM = 0x00,
USE_VIRTUAL_RAM = 0x01
};
typedef struct {
// Main memory, linear but indexed as though it were banks in 1K pages.
uint8_t memory[Z80_VIRTUAL_MEMORY_SIZE];
// Page pointer map.
//
// Each pointer points to a byte or block of bytes in the Z80 Memory frame, 64K Real + Banked.
// This is currently set at a block of size 0x800 per memory pointer for the MZ-700.
// The LSB of the pointer is a direct memory index to a byte or block of bytes, the upper byte of the pointer indicates type of memory space.
// 0x80<FFFFFF> - physical host RAM
// 0x40<FFFFFF> - physical host ROM
// 0x20<FFFFFF> - physical host VRAM
// 0x10<FFFFFF> - physical host hardware
// 0x08<FFFFFF> - virtual host RAM
// 0x04<FFFFFF> - virtual host ROM
// 0x02<FFFFFF> - virtual host hardware
// 16bit Input Address -> map -> Pointer to 24bit memory address + type flag.
// -> Pointer+<low bits of address> to 24bit memory address + type flag.
uint32_t page[MEMORY_BLOCK_SLOTS];
uint32_t shadowPage[MEMORY_BLOCK_SLOTS];
// I/O Page map.
//
// This is a map to indicate the use of the I/O page and allow any required remapping.
// <0x80>FF<I/O Address> - physical host hardware
// <0x40>FF<I/O Address> - virtual host hardware
// 16bit Input Address -> map -> Actual 16bit address to use + type flag.
uint32_t iopage[65536];
// Default page mode configured. This value reflects the default page and iotable map.
uint8_t defaultPageMode;
// Refresh DRAM mode. 1 = Refresh, 0 = No refresh. Only applicable when running code in virtual Kernel RAM.
uint8_t refreshDRAM;
// Inhibit mode is where certain memory ranges are inhibitted. The memory page is set to inhibit and this flag
// blocks actions which arent allowed during inhibit.
uint8_t inhibitMode;
// Address caching. Used to minimise instruction length sent to CPLD.
uint16_t z80PrevAddr;
uint16_t z80PrevPort;
#if(TARGET_HOST_MZ2000 == 1)
uint8_t lowMemorySwap;
#endif
// Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe;
uint8_t keyportShiftCtrl;
uint8_t keyportHotKey;
// Governor is the delay in a 32bit loop per Z80 opcode, used to govern execution speed when using virtual memory.
// This mechanism will eventually be tied into the M/T-state calculation for a more precise delay, but at the moment,
// with the Z80 assigned to an isolated CPU, it allows time sensitive tasks such as the tape recorder to work.
// The lower the value the faster the CPU speed. Two values are present as the optimiser, seeing ROM code not changing
// is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made.
uint32_t cpuGovernorDelayROM;
uint32_t cpuGovernorDelayRAM;
} t_Z80Ctrl;
// IOCTL structure for passing data from user space to driver to perform commands.
//
struct z80_addr {
uint32_t start;
uint32_t end;
uint32_t size;
};
struct z80_ctrl {
uint16_t pc;
};
struct speed {
uint32_t speedMultiplier;
};
struct cpld_ctrl {
uint32_t cmd;
};
struct ioctlCmd {
int32_t cmd;
union {
struct z80_addr addr;
struct z80_ctrl z80;
struct speed speed;
struct cpld_ctrl cpld;
};
};
// Prototypes.
void setupMemory(enum Z80_MEMORY_PROFILE mode);
#endif

View File

@@ -1,428 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the methods used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
//-------------------------------------------------------------------------------------------------------------------------------
//
// User space driver access.
//
//-------------------------------------------------------------------------------------------------------------------------------
// Initialise the SOM hardware used to communicate with the z80 socket and host hardware.
// The SOM interfaces to a CPLD which provides voltage level translation and also encapsulates the Z80 timing cycles as recreating
// them within the SOM is much more tricky.
//
// As this is an embedded device and performance/latency are priorities, minimal structured code is used to keep call stack and
// generated code to a mimimum without relying on the optimiser.
int z80io_init(void)
{
// Locals.
int ret = 0;
// Initialise GPIO. We call the HAL api to minimise time but for actual bit set/reset and read we go directly to registers to save time, increase throughput and minimise latency.
// Initialise the HAL.
MHal_GPIO_Init();
// Set the pads as GPIO devices. The HAL takes care of allocating and deallocating the padmux resources.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_0); // Word (16bit) bidirectional bus. Default is read with data set.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_HIGH_BYTE);
//MHal_GPIO_Pad_Set(PAD_GPIO8); // SPIO 4wire control lines setup by the spidev driver but controlled directly in this driver.
//MHal_GPIO_Pad_Set(PAD_GPIO9);
//MHal_GPIO_Pad_Set(PAD_GPIO10);
//MHal_GPIO_Pad_Set(PAD_GPIO11);
MHal_GPIO_Pad_Set(PAD_Z80IO_READY);
MHal_GPIO_Pad_Set(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Set(PAD_Z80IO_INT);
MHal_GPIO_Pad_Set(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Set(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Set(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Set(PAD_Z80IO_RSV1);
#ifdef NOTNEEDED
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_WRITE);
#endif
// Set required input pads.
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_0);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Odn(PAD_Z80IO_READY);
MHal_GPIO_Pad_Odn(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Odn(PAD_Z80IO_INT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Odn(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RSV1);
// Set required output pads.
#ifdef NOTNEEDED
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Oen(PAD_Z80IO_WRITE);
MHal_GPIO_Pull_High(PAD_Z80IO_WRITE);
#endif
// Control signals.
MHal_GPIO_Pad_Oen(PAD_Z80IO_HIGH_BYTE);
MHal_GPIO_Pull_High(PAD_Z80IO_HIGH_BYTE);
// Setup the MSPI0 device.
//
// Setup control, interrupts are not used.
MSPI_WRITE(MSPI_CTRL_OFFSET, MSPI_CPU_CLOCK_1_2 | MSPI_CTRL_CPOL_LOW | MSPI_CTRL_CPHA_HIGH | MSPI_CTRL_RESET | MSPI_CTRL_ENABLE_SPI);
// Setup LSB First mode.
MSPI_WRITE(MSPI_LSB_FIRST_OFFSET, 0x0);
// Setup clock.
CLK_WRITE(MSPI0_CLK_CFG, 0x1100)
// Setup the frame size (all buffers to 8bits).
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET+1, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET+1, 0xfff);
// Setup Chip Selects to inactive.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Switch Video and Audio to host.
z80io_SPI_Send16(0x00f0, NULL);
return ret;
}
//--------------------------------------------------------
// Parallel bus Methods.
//--------------------------------------------------------
// Methods to read data from the parallel bus.
// The CPLD returns status and Z80 data on the 8bit bus as it is marginally quicker than retrieving it over the SPI bus.
//
inline uint8_t z80io_PRL_Read8(uint8_t dataFlag)
{
// Locals.
uint8_t result = 0;
// Byte according to flag.
if(dataFlag)
SET_CPLD_READ_DATA()
else
SET_CPLD_READ_STATUS()
// Read the input registers and set value accordingly.
result = READ_CPLD_DATA_IN();
// Return 16bit value read from CPLD.
return(result);
}
inline uint16_t z80io_PRL_Read16(void)
{
// Locals.
uint16_t result = 0;
// Low byte first.
CLEAR_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result = (uint16_t)READ_CPLD_DATA_IN();
// High byte next.
SET_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result |= (uint16_t)(READ_CPLD_DATA_IN() << 8);
// Return 16bit value read from CPLD.
return(result);
}
// Parallel Bus methods were tried and tested but due to the GPIO bits being controlled by individual registers per bit, the setup time was longer
// than the transmission time of SPI. These methods are thus deprecated and a fusion of SPI and 8bit parallel is now used.
#ifdef NOTNEEDED
inline uint8_t z80io_PRL_Send8(uint8_t txData)
{
// Locals.
//
// Low byte only.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
inline uint8_t z80io_PRL_Send16(uint16_t txData)
{
// Locals.
//
// Low byte first.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
// High byte next.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
// Setup high byte.
if(txData & 0x8000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x4000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x2000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x1000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0800) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0400) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0200) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0100) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
#endif
//--------------------------------------------------------
// SPI Methods.
//--------------------------------------------------------
// Methods to send 8,16 or 32 bits. Each method is seperate to minimise logic and execution time, 8bit being most sensitive.
// Macros have also been defined for inline inclusion which dont read back the response data.
//
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint8_t)MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)(txData >> 16));
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint32_t)(MSPI_READ(MSPI_FULL_DEPLUX_RD00) | (MSPI_READ(MSPI_FULL_DEPLUX_RD02) << 16));
// Done.
return(timeout == 0);
}
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)
{
pr_info("Z80 Test Memory functionality not built-in.\n");
return(0);
}
uint8_t z80io_SPI_Test(void)
{
pr_info("SPI Test functionality not built-in.\n");
return(0);
}
uint8_t z80io_PRL_Test(void)
{
pr_info("Parallel Bus Test functionality not built-in.\n");
return(0);
}
#endif

View File

@@ -1,483 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the declarations used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80IO_H
#define Z80IO_H
#ifdef __cplusplus
extern "C" {
#endif
// Definitions to control compilation.
#define INCLUDE_TEST_METHODS 1
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
#define CPLD_CMD_FETCH_ADDR_P1 0x11
#define CPLD_CMD_FETCH_ADDR_P2 0x12
#define CPLD_CMD_FETCH_ADDR_P3 0x13
#define CPLD_CMD_FETCH_ADDR_P4 0x14
#define CPLD_CMD_FETCH_ADDR_P5 0x15
#define CPLD_CMD_FETCH_ADDR_P6 0x16
#define CPLD_CMD_FETCH_ADDR_P7 0x17
#define CPLD_CMD_WRITE_ADDR 0x18
#define CPLD_CMD_WRITE_ADDR_P1 0x19
#define CPLD_CMD_WRITE_ADDR_P2 0x1A
#define CPLD_CMD_WRITE_ADDR_P3 0x1B
#define CPLD_CMD_WRITE_ADDR_P4 0x1C
#define CPLD_CMD_WRITE_ADDR_P5 0x1D
#define CPLD_CMD_WRITE_ADDR_P6 0x1E
#define CPLD_CMD_WRITE_ADDR_P7 0x1F
#define CPLD_CMD_READ_ADDR 0x20
#define CPLD_CMD_READ_ADDR_P1 0x21
#define CPLD_CMD_READ_ADDR_P2 0x22
#define CPLD_CMD_READ_ADDR_P3 0x23
#define CPLD_CMD_READ_ADDR_P4 0x24
#define CPLD_CMD_READ_ADDR_P5 0x25
#define CPLD_CMD_READ_ADDR_P6 0x26
#define CPLD_CMD_READ_ADDR_P7 0x27
#define CPLD_CMD_WRITEIO_ADDR 0x28
#define CPLD_CMD_WRITEIO_ADDR_P1 0x29
#define CPLD_CMD_WRITEIO_ADDR_P2 0x2A
#define CPLD_CMD_WRITEIO_ADDR_P3 0x2B
#define CPLD_CMD_WRITEIO_ADDR_P4 0x2C
#define CPLD_CMD_WRITEIO_ADDR_P5 0x2D
#define CPLD_CMD_WRITEIO_ADDR_P6 0x2E
#define CPLD_CMD_WRITEIO_ADDR_P7 0x2F
#define CPLD_CMD_READIO_ADDR 0x30
#define CPLD_CMD_READIO_ADDR_P1 0x31
#define CPLD_CMD_READIO_ADDR_P2 0x32
#define CPLD_CMD_READIO_ADDR_P3 0x33
#define CPLD_CMD_READIO_ADDR_P4 0x34
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
#define CPLD_CMD_SET_AUTO_REFRESH 0xF1
#define CPLD_CMD_CLEAR_AUTO_REFRESH 0xF2
#define CPLD_CMD_SET_SPI_LOOPBACK 0xFE
#define CPLD_CMD_NOP1 0x00
#define CPLD_CMD_NOP2 0xFF
// Pad numbers for using the MHal GPIO library.
#define PAD_Z80IO_IN_DATA_0 PAD_GPIO0
#define PAD_Z80IO_IN_DATA_1 PAD_GPIO1
#define PAD_Z80IO_IN_DATA_2 PAD_GPIO2
#define PAD_Z80IO_IN_DATA_3 PAD_GPIO3
#define PAD_Z80IO_IN_DATA_4 PAD_GPIO4
#define PAD_Z80IO_IN_DATA_5 PAD_GPIO5
#define PAD_Z80IO_IN_DATA_6 PAD_GPIO6
#define PAD_Z80IO_IN_DATA_7 PAD_GPIO7
#define PAD_SPIO_0 PAD_GPIO8
#define PAD_SPIO_1 PAD_GPIO9
#define PAD_SPIO_2 PAD_GPIO10
#define PAD_SPIO_3 PAD_GPIO11
#define PAD_Z80IO_HIGH_BYTE PAD_SAR_GPIO2 // Byte requiured, 0 = Low Byte, 1 = High Byte.
#define PAD_Z80IO_READY PAD_GPIO12
#define PAD_Z80IO_LTSTATE PAD_PM_IRIN // IRIN
#define PAD_Z80IO_BUSRQ PAD_GPIO13
#define PAD_Z80IO_BUSACK PAD_GPIO14
#define PAD_Z80IO_INT PAD_UART0_RX // GPIO47
#define PAD_Z80IO_NMI PAD_UART0_TX // GPIO48
#define PAD_Z80IO_WAIT PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_RESET PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_RSV1 PAD_SATA_GPIO // GPIO90
// Physical register addresses.
#define PAD_Z80IO_IN_DATA_0_ADDR 0x103C00
#define PAD_Z80IO_IN_DATA_1_ADDR 0x103C02
#define PAD_Z80IO_IN_DATA_2_ADDR 0x103C04
#define PAD_Z80IO_IN_DATA_3_ADDR 0x103C06
#define PAD_Z80IO_IN_DATA_4_ADDR 0x103C08
#define PAD_Z80IO_IN_DATA_5_ADDR 0x103C0A
#define PAD_Z80IO_IN_DATA_6_ADDR 0x103C0C
#define PAD_Z80IO_IN_DATA_7_ADDR 0x103C0E
#define PAD_SPIO_0_ADDR 0x103C10
#define PAD_SPIO_1_ADDR 0x103C12
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_LTSTATE_ADDR 0xF28 // IRIN
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
#define PAD_Z80IO_INT_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_NMI_ADDR 0x103C32 // GPIO48
#define PAD_Z80IO_WAIT_ADDR 0x103C80 // GPIO85
#define PAD_Z80IO_RESET_ADDR 0x103C82 // GPIO86
#define PAD_Z80IO_RSV1_ADDR 0x103C8A // GPIO90
#ifdef NOTNEEDED
#define PAD_Z80IO_OUT_DATA_0 PAD_GPIO12
#define PAD_Z80IO_OUT_DATA_1 PAD_GPIO13
#define PAD_Z80IO_OUT_DATA_2 PAD_GPIO14
#define PAD_Z80IO_OUT_DATA_3 PAD_UART0_RX // GPIO47
#define PAD_Z80IO_OUT_DATA_4 PAD_UART0_TX // GPIO48
#define PAD_Z80IO_OUT_DATA_5 PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_OUT_DATA_6 PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_OUT_DATA_7 PAD_SATA_GPIO // GPIO90
#define PAD_Z80IO_WRITE PAD_PM_IRIN // Write data clock.
#endif
//-------------------------------------------------------------------------------------------------
// The definitions below come from SigmaStar kernel drivers. No header file exists hence the
// duplication.
//-------------------------------------------------------------------------------------------------
#define SUPPORT_SPI_1 0
#define MAX_SUPPORT_BITS 16
#define BANK_TO_ADDR32(b) (b<<9)
#define BANK_SIZE 0x200
#define MS_BASE_REG_RIU_PA 0x1F000000
#define gChipBaseAddr 0xFD203C00
#define gPmSleepBaseAddr 0xFD001C00
#define gSarBaseAddr 0xFD002800
#define gRIUBaseAddr 0xFD000000
#define gMOVDMAAddr 0xFD201600
#define gClkBaseAddr 0xFD207000
#define gMspBaseAddr 0xfd222000
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MSPI0_BANK_ADDR 0x1110
#define MSPI1_BANK_ADDR 0x1111
#define CLK__BANK_ADDR 0x1038
#define CHIPTOP_BANK_ADDR 0x101E
#define MOVDMA_BANK_ADDR 0x100B
#define BASE_REG_MSPI0_ADDR MSPI0_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111000)
#define BASE_REG_MSPI1_ADDR MSPI1_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111100)
#define BASE_REG_CLK_ADDR CLK__BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x103800)
#define BASE_REG_CHIPTOP_ADDR CHIPTOP_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x101E00)
//-------------------------------------------------------------------------------------------------
// Hardware Register Capability
//-------------------------------------------------------------------------------------------------
#define MSPI_WRITE_BUF_OFFSET 0x40
#define MSPI_READ_BUF_OFFSET 0x44
#define MSPI_WBF_SIZE_OFFSET 0x48
#define MSPI_RBF_SIZE_OFFSET 0x48
// read/ write buffer size
#define MSPI_RWSIZE_MASK 0xFF
#define MSPI_RSIZE_BIT_OFFSET 0x8
#define MAX_READ_BUF_SIZE 0x8
#define MAX_WRITE_BUF_SIZE 0x8
// CLK config
#define MSPI_CTRL_OFFSET 0x49
#define MSPI_CLK_CLOCK_OFFSET 0x49
#define MSPI_CLK_CLOCK_BIT_OFFSET 0x08
#define MSPI_CLK_CLOCK_MASK 0xFF
#define MSPI_CLK_PHASE_MASK 0x40
#define MSPI_CLK_PHASE_BIT_OFFSET 0x06
#define MSPI_CLK_POLARITY_MASK 0x80
#define MSPI_CLK_POLARITY_BIT_OFFSET 0x07
#define MSPI_CLK_PHASE_MAX 0x1
#define MSPI_CLK_POLARITY_MAX 0x1
#define MSPI_CLK_CLOCK_MAX 0x7
#define MSPI_CTRL_CPOL_LOW 0x00
#define MSPI_CTRL_CPOL_HIGH 0x80
#define MSPI_CTRL_CPHA_LOW 0x00
#define MSPI_CTRL_CPHA_HIGH 0x40
#define MSPI_CTRL_3WIRE 0x10
#define MSPI_CTRL_INTEN 0x04
#define MSPI_CTRL_RESET 0x02
#define MSPI_CTRL_ENABLE_SPI 0x01
// DC config
#define MSPI_DC_MASK 0xFF
#define MSPI_DC_BIT_OFFSET 0x08
#define MSPI_DC_TR_START_OFFSET 0x4A
#define MSPI_DC_TRSTART_MAX 0xFF
#define MSPI_DC_TR_END_OFFSET 0x4A
#define MSPI_DC_TREND_MAX 0xFF
#define MSPI_DC_TB_OFFSET 0x4B
#define MSPI_DC_TB_MAX 0xFF
#define MSPI_DC_TRW_OFFSET 0x4B
#define MSPI_DC_TRW_MAX 0xFF
// Frame Config
#define MSPI_FRAME_WBIT_OFFSET 0x4C
#define MSPI_FRAME_RBIT_OFFSET 0x4E
#define MSPI_FRAME_BIT_MAX 0x07
#define MSPI_FRAME_BIT_MASK 0x07
#define MSPI_FRAME_BIT_FIELD 0x03
#define MSPI_LSB_FIRST_OFFSET 0x50
#define MSPI_TRIGGER_OFFSET 0x5A
#define MSPI_DONE_OFFSET 0x5B
#define MSPI_DONE_CLEAR_OFFSET 0x5C
#define MSPI_CHIP_SELECT_OFFSET 0x5F
#define MSPI_CS1_DISABLE 0x01
#define MSPI_CS1_ENABLE 0x00
#define MSPI_CS2_DISABLE 0x02
#define MSPI_CS2_ENABLE 0x00
#define MSPI_CS3_DISABLE 0x04
#define MSPI_CS3_ENABLE 0x00
#define MSPI_CS4_DISABLE 0x08
#define MSPI_CS4_ENABLE 0x00
#define MSPI_CS5_DISABLE 0x10
#define MSPI_CS5_ENABLE 0x00
#define MSPI_CS6_DISABLE 0x20
#define MSPI_CS6_ENABLE 0x00
#define MSPI_CS7_DISABLE 0x40
#define MSPI_CS7_ENABLE 0x00
#define MSPI_CS8_DISABLE 0x80
#define MSPI_CS8_ENABLE 0x00
#define MSPI_FULL_DEPLUX_RD_CNT (0x77)
#define MSPI_FULL_DEPLUX_RD00 (0x78)
#define MSPI_FULL_DEPLUX_RD01 (0x78)
#define MSPI_FULL_DEPLUX_RD02 (0x79)
#define MSPI_FULL_DEPLUX_RD03 (0x79)
#define MSPI_FULL_DEPLUX_RD04 (0x7a)
#define MSPI_FULL_DEPLUX_RD05 (0x7a)
#define MSPI_FULL_DEPLUX_RD06 (0x7b)
#define MSPI_FULL_DEPLUX_RD07 (0x7b)
#define MSPI_FULL_DEPLUX_RD08 (0x7c)
#define MSPI_FULL_DEPLUX_RD09 (0x7c)
#define MSPI_FULL_DEPLUX_RD10 (0x7d)
#define MSPI_FULL_DEPLUX_RD11 (0x7d)
#define MSPI_FULL_DEPLUX_RD12 (0x7e)
#define MSPI_FULL_DEPLUX_RD13 (0x7e)
#define MSPI_FULL_DEPLUX_RD14 (0x7f)
#define MSPI_FULL_DEPLUX_RD15 (0x7f)
//chip select bit map
#define MSPI_CHIP_SELECT_MAX 0x07
// control bit
#define MSPI_DONE_FLAG 0x01
#define MSPI_TRIGGER 0x01
#define MSPI_CLEAR_DONE 0x01
#define MSPI_INT_ENABLE 0x04
#define MSPI_RESET 0x02
#define MSPI_ENABLE 0x01
// clk_mspi0
#define MSPI0_CLK_CFG 0x33 //bit 2 ~bit 3
#define MSPI0_CLK_108M 0x00
#define MSPI0_CLK_54M 0x04
#define MSPI0_CLK_12M 0x08
#define MSPI0_CLK_MASK 0x0F
// clk_mspi1
#define MSPI1_CLK_CFG 0x33 //bit 10 ~bit 11
#define MSPI1_CLK_108M 0x0000
#define MSPI1_CLK_54M 0x0400
#define MSPI1_CLK_12M 0x0800
#define MSPI1_CLK_MASK 0x0F00
// clk_mspi
#define MSPI_CLK_CFG 0x33
#define MSPI_SELECT_0 0x0000
#define MSPI_SELECT_1 0x4000
#define MSPI_CLK_MASK 0xF000
// Clock settings
#define MSPI_CPU_CLOCK_1_2 0x0000
#define MSPI_CPU_CLOCK_1_4 0x0100
#define MSPI_CPU_CLOCK_1_8 0x0200
#define MSPI_CPU_CLOCK_1_16 0x0300
#define MSPI_CPU_CLOCK_1_32 0x0400
#define MSPI_CPU_CLOCK_1_64 0x0500
#define MSPI_CPU_CLOCK_1_128 0x0600
#define MSPI_CPU_CLOCK_1_256 0x0700
//CHITOP 101E mspi mode select
#define MSPI0_MODE 0x0C //bit0~bit1
#define MSPI0_MODE_MASK 0x07
#define MSPI1_MODE 0x0C //bit4~bit5
#define MSPI1_MODE_MASK 0x70
#define EJTAG_MODE 0xF
#define EJTAG_MODE_1 0x01
#define EJTAG_MODE_2 0x02
#define EJTAG_MODE_3 0x03
#define EJTAG_MODE_MASK 0x03
//MOVDMA 100B
#define MOV_DMA_SRC_ADDR_L 0x03
#define MOV_DMA_SRC_ADDR_H 0x04
#define MOV_DMA_DST_ADDR_L 0x05
#define MOV_DMA_DST_ADDR_H 0x06
#define MOV_DMA_BYTE_CNT_L 0x07
#define MOV_DMA_BYTE_CNT_H 0x08
#define DMA_MOVE0_IRQ_CLR 0x28
#define MOV_DMA_IRQ_FINAL_STATUS 0x2A
#define DMA_MOVE0_ENABLE 0x00
#define DMA_RW 0x50 //0 for dma write to device, 1 for dma read from device
#define DMA_READ 0x01
#define DMA_WRITE 0x00
#define DMA_DEVICE_MODE 0x51
#define DMA_DEVICE_SEL 0x52
//spi dma
#define MSPI_DMA_DATA_LENGTH_L 0x30
#define MSPI_DMA_DATA_LENGTH_H 0x31
#define MSPI_DMA_ENABLE 0x32
#define MSPI_DMA_RW_MODE 0x33
#define MSPI_DMA_WRITE 0x00
#define MSPI_DMA_READ 0x01
#define MSTAR_SPI_TIMEOUT_MS 30000
#define MSTAR_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA /*| SPI_CS_HIGH | SPI_NO_CS | SPI_LSB_FIRST*/)
//-------------------------------------------------------------------------------------------------
// Macros
//-------------------------------------------------------------------------------------------------
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define READ_BYTE(_reg) (*(volatile u8*)(_reg))
#define READ_WORD(_reg) (*(volatile u16*)(_reg))
#define READ_LONG(_reg) (*(volatile u32*)(_reg))
#define WRITE_BYTE(_reg, _val) {(*((volatile u8*)(_reg))) = (u8)(_val); }
#define WRITE_WORD(_reg, _val) {(*((volatile u16*)(_reg))) = (u16)(_val); }
#define WRITE_LONG(_reg, _val) {(*((volatile u32*)(_reg))) = (u32)(_val); }
#define WRITE_WORD_MASK(_reg, _val, _mask) {(*((volatile u16*)(_reg))) = ((*((volatile u16*)(_reg))) & ~(_mask)) | ((u16)(_val) & (_mask)); }
#define READ_CPLD_DATA_IN() ((MHal_RIU_REG(PAD_Z80IO_IN_DATA_7_ADDR) & 0x1) << 7 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_6_ADDR) & 0x1) << 6 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_5_ADDR) & 0x1) << 5 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_4_ADDR) & 0x1) << 4 |\
(MHal_RIU_REG(PAD_Z80IO_IN_DATA_3_ADDR) & 0x1) << 3 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_2_ADDR) & 0x1) << 2 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_1_ADDR) & 0x1) << 1 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_0_ADDR) & 0x1))
#define SET_CPLD_READ_DATA() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define SET_CPLD_READ_STATUS() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define SET_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define CLEAR_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define CPLD_READY() (MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1)
#define CPLD_RESET() (MHal_RIU_REG(PAD_Z80IO_RESET_ADDR) & 0x1)
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte
//#define MSPI_WRITE(_reg_, _val_) {pr_info("PDS: MSPI_WRITE(0x%x, 0x%x, 0x%x)\n", _reg_, _val_, gMspBaseAddr + ((_reg_)<<2)); WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_)); }
#define MSPI_WRITE(_reg_, _val_) WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_));
//write 2 byte mask
//#define MSPI_WRITE_MASK(_reg_, _val_, mask) {pr_info("PDS: WRITE_LONG(0x%x, 0x%x, mask=0x%x)\n", _reg_, _val_, mask); WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask)); }
#define MSPI_WRITE_MASK(_reg_, _val_, mask) WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask));
#define CLK_READ(_reg_) READ_WORD(gClkBaseAddr + ((_reg_)<<2))
//#define CLK_WRITE(_reg_, _val_) {pr_info("PDS: CLK_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_)); }
#define CLK_WRITE(_reg_, _val_) WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_));
#define CHIPTOP_READ(_reg_) READ_WORD(gChipBaseAddr + ((_reg_)<<2))
//#define CHIPTOP_WRITE(_reg_, _val_) {pr_info("PDS: CHIPTOP_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_)); }
#define CHIPTOP_WRITE(_reg_, _val_) WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_));
#define MOVDMA_READ(_reg_) READ_WORD(gMOVDMAAddr + ((_reg_)<<2))
//#define MOVDMA_WRITE(_reg_, _val_) {pr_info("PDS: MOVDMA_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_)); }
#define MOVDMA_WRITE(_reg_, _val_) WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_));
#define _HAL_MSPI_ClearDone() MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET,MSPI_CLEAR_DONE)
#define MAX_CHECK_CNT 2000
#define MSPI_READ_INDEX 0x0
#define MSPI_WRITE_INDEX 0x1
#define SPI_MIU0_BUS_BASE 0x20000000
#define SPI_MIU1_BUS_BASE 0xFFFFFFFF
// Function definitions.
//
int z80io_init(void);
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData);
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData);
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData);
#ifdef NOTNEEDED
uint8_t z80io_PRL_Send8(uint8_t txData);
uint8_t z680io_PRL_Send16(uint16_t txData);
#endif
uint8_t z80io_PRL_Read8(uint8_t dataFlag);
uint16_t z80io_PRL_Read16(void);
uint8_t z80io_SPI_Test(void);
uint8_t z80io_PRL_Test(void);
uint8_t z80io_Z80_TestMemory(void);
extern void MHal_GPIO_Init(void);
extern void MHal_GPIO_Pad_Set(uint8_t u8IndexGPIO);
extern int MHal_GPIO_PadGroupMode_Set(uint32_t u32PadMode);
extern int MHal_GPIO_PadVal_Set(uint8_t u8IndexGPIO, uint32_t u32PadMode);
extern void MHal_GPIO_Pad_Oen(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pad_Odn(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_Level(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_InOut(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_Low(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_Low(uint8_t u8IndexGPIO);
extern void MHal_Enable_GPIO_INT(uint8_t u8IndexGPIO);
extern int MHal_GPIO_To_Irq(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_POLARITY(uint8_t u8IndexGPIO, uint8_t reverse);
extern void MHal_GPIO_Set_Driving(uint8_t u8IndexGPIO, uint8_t setHigh);
extern void MHal_GPIO_PAD_32K_OUT(uint8_t u8Enable);
#ifdef __cplusplus
}
#endif
#endif // Z80IO_H

View File

@@ -1,541 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io_test.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface Test Methods
// This file contains the methods used to test the SOM to CPLD interface and evaluate
// it's performance. Production builds wont include these methods.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/sched.h>
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
uint8_t z80io_Z80_TestMemory(void)
{
// Locals.
//
uint32_t addr;
uint32_t fullCmd;
uint8_t cmd;
struct timeval start, stop;
uint32_t iterations = 100;
uint32_t errorCount;
uint32_t idx;
long totalTime;
long bytesMSec;
uint8_t result;
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E80000 | (0x82 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E20000 | (0x58 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E00000 | (0xF7 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E90000 | (0x0F << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xCF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xFF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
pr_info("Z80 Host Test - IO.\n");
// for(idx=0; idx < 1000000; idx++)
// {
// SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
// SPI_SEND32(0xD0000000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0100000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0200000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0300000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0400000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0500000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// }
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000| ((uint8_t)addr) << 8 | CPLD_CMD_WRITEIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Read performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible IO ports and write to it.
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000 | ((uint8_t)addr) << 8 | CPLD_CMD_READIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing RAM Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write performance (opt).\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Fetch performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Read performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Fetch performance.\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x10;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Read performance (opt).\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x20;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
// Go through all the accessible attribute VRAM and initialise it.
pr_info("Z80 Host Test - Testing VRAM Write performance.\n");
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
iterations = 256*10;
do_gettimeofday(&start);
for(addr=0xD800; addr < 0xE000; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD800)
{
fullCmd = (addr << 16) |(0x71 << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible VRAM and write to it.
for(addr=0xD000; addr < 0xD800; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD000)
{
fullCmd = (addr << 16) | ((uint8_t)idx << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)((1*iterations*0x800)+0x800)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
return(0);
}
// A simple test to verify the SOM to CPLD SPI connectivity and give an estimate of its performance.
// The performance is based on the SPI setup and transmit time along with the close and received data processing.
// In real use, the driver will just send a command and generally ignore received data so increased throughput can be achieved.
//
uint8_t z80io_SPI_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
uint16_t rxData16Last;
uint32_t rxData32;
uint32_t rxData32Last;
uint32_t errorCount;
long totalTime;
long bytesMSec;
// Place the CPLD into echo test mode.
z80io_SPI_Send8(0xfe, &rxData8);
// 1st. test, 8bit.
pr_info("SPI Test - Testing 8 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send8((uint8_t)idx, &rxData8);
if(idx > 1 && (uint8_t)(idx-1) != rxData8)
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint8_t)(idx-1), rxData8 );
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 16bit.
pr_info("SPI Test - Testing 16 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_SPI_Send16((uint16_t)idx, &rxData16);
if(idx > 0 && (uint16_t)(idx-1) != (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint16_t)(idx-1), (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)));
errorCount++;
}
rxData16Last = rxData16;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 3rd. test, 32bit.
pr_info("SPI Test - Testing 32 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send32((uint32_t)idx, &rxData32);
if(idx > 0 && (uint32_t)(idx-1) != (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint32_t)(idx-1), (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)));
errorCount++;
}
rxData32Last = rxData32;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(4*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}
// Method to test the parallel bus, verifying integrity and assessing performance.
uint8_t z80io_PRL_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
long totalTime;
long bytesMSec;
#ifdef NOTNEEDED
uint32_t errorCount;
#endif
// Place the CPLD into echo test mode.
// 1st. test, 8bit RW.
#ifdef NOTNEEDED
pr_info("Parallel Test - Testing 8 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte and readback to compare.
z80io_PRL_Send8((uint8_t)idx);
rxData8 = z80io_PRL_Read8();
if((uint8_t)idx != rxData8)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint8_t)(idx), rxData8);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 8bit Write.
pr_info("Parallel Test - Testing 8 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte.
z80io_PRL_Send8((uint8_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 3rd. test, 8bit Read.
pr_info("Parallel Test - Testing 8 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read byte.
rxData8 = z80io_PRL_Read8(0);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#ifdef NOTNEEDED
// 4th test, 16bit.
pr_info("Parallel Test - Testing 16 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_PRL_Send16((uint16_t)idx);
rxData16 = z80io_PRL_Read16();
if((uint16_t)idx != rxData16)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint16_t)(idx), rxData16);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 5th test, 16bit Write.
pr_info("Parallel Test - Testing 16 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write word.
z80io_PRL_Send16((uint16_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 6th test, 16bit Read.
pr_info("Parallel Test - Testing 16 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read word.
rxData16 = z80io_PRL_Read16();
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}

View File

@@ -1,403 +0,0 @@
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname && longopts[i].shortname < 127) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */

View File

@@ -1,734 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80ctrl.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Control Interface
// This file contains a command line utility tool for controlling the z80drv device
// driver. The tool allows manipulation of the emulated Z80, inspection of its
// memory and data, transmission of adhoc commands to the underlying CPLD-Z80
// gateway and loading/saving of programs and data to/from the Z80 virtual and
// host memory.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of the Z80 device driver.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <Z/constants/pointer.h>
#include <Z/macros/member.h>
#include <Z/macros/array.h>
#include <Z80.h>
#include "z80driver.h"
#define VERSION "1.0"
#define AUTHOR "P.D.Smart"
#define COPYRIGHT "(c) 2018-22"
// Getopt_long is buggy so we use optparse.
#define OPTPARSE_IMPLEMENTATION
#define OPTPARSE_API static
#include "optparse.h"
// Device driver name.
#define DEVICE_FILENAME "/dev/z80drv"
// Constants for the Sharp MZ80A MZF file format.
#define MZF_HEADER_SIZE 128 // Size of the MZF header.
#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code.
#define MZF_FILENAME 0x01 // Title/Name (17 bytes).
#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed.
#define MZF_FILESIZE 0x12 // Size of program.
#define MZF_LOADADDR 0x14 // Load address of program.
#define MZF_EXECADDR 0x16 // Exec address of program.
#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code.
#define MZF_COMMENT_LEN 104 // Length of the comment field.
#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object.
#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program.
#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program.
#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0.
#define CMT_TYPE_TZOBJCD1 0x0F9
#define CMT_TYPE_TZOBJCD2 0x0FA
#define CMT_TYPE_TZOBJCD3 0x0FB
#define CMT_TYPE_TZOBJCD4 0x0FC
#define CMT_TYPE_TZOBJCD5 0x0FD
#define CMT_TYPE_TZOBJCD6 0x0FE
#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7.
#define MZ_CMT_ADDR 0x10F0
// Structure to define a Sharp MZ80A MZF directory structure. This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images.
//
typedef struct __attribute__((__packed__)) {
uint8_t attr; // MZF attribute describing the file.
uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename.
uint16_t fileSize; // Size of file.
uint16_t loadAddr; // Load address for the file.
uint16_t execAddr; // Execution address where the Z80 starts processing.
uint8_t comment[MZF_COMMENT_LEN]; // Text comment field but often contains a startup machine code program.
} t_svcDirEnt;
// Possible commands to be issued to the Z80 driver.
enum CTRL_COMMANDS {
Z80_CMD_STOP = 0,
Z80_CMD_START = 1,
Z80_CMD_PAUSE = 2,
Z80_CMD_CONTINUE = 3,
Z80_CMD_RESET = 4,
Z80_CMD_SPEED = 5,
Z80_CMD_HOST_RAM = 6,
Z80_CMD_VIRTUAL_RAM = 7,
Z80_CMD_DUMP_MEMORY = 8,
Z80_CMD_MEMORY_TEST = 9,
CPLD_CMD_SEND_CMD = 10,
CPLD_CMD_SPI_TEST = 11,
CPLD_CMD_PRL_TEST = 12
};
// Shared memory between this process and the Z80 driver.
static t_Z80Ctrl *Z80Ctrl = NULL;
// Method to obtain and return the output screen width.
//
uint8_t getScreenWidth(void)
{
return(MAX_SCREEN_WIDTH);
}
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch(uint8_t wait)
{
int r;
unsigned char c;
if(wait != 0 || (wait == 0 && kbhit()))
{
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
return 0;
}
void delay(int number_of_seconds)
{
// Converting time into milli_seconds
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
}
// Function to dump out a given section of memory via the UART.
//
int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryFlag, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth)
{
uint8_t displayWidth = dispwidth;;
uint32_t pnt = memaddr;
uint32_t endAddr = memaddr + memsize;
uint32_t addr = dispaddr;
uint32_t i = 0;
//uint32_t data;
int8_t keyIn;
int result = -1;
char c = 0;
// Sanity check. memoryFlag == 0 required kernel driver to dump so we exit as it cannot be performed here.
if(memoryFlag == 0)
return(-1);
// Reconfigure terminal to allow non-blocking key input.
//
set_conio_terminal_mode();
// If not set, calculate output line width according to connected display width.
//
if(displayWidth == 0)
{
switch(getScreenWidth())
{
case 40:
displayWidth = 8;
break;
case 80:
displayWidth = 16;
break;
default:
displayWidth = 32;
break;
}
}
while (1)
{
printf("%08lX", addr); // print address
printf(": ");
// print hexadecimal data
for (i=0; i < displayWidth; )
{
switch(memwidth)
{
case 16:
if(pnt+i < endAddr)
printf("%04X", memoryFlag == 1 ? (uint16_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint16_t)Z80Ctrl->page[pnt+i] : (uint16_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 32:
if(pnt+i < endAddr)
printf("%08lX", memoryFlag == 1 ? (uint32_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint32_t)Z80Ctrl->page[pnt+i] : (uint32_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 8:
default:
if(pnt+i < endAddr)
printf("%02X", memoryFlag == 1 ? (uint8_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint8_t)Z80Ctrl->page[pnt+i] : (uint8_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
}
fputc((char)' ', stdout);
}
// print ascii data
printf(" |");
// print single ascii char
for (i=0; i < displayWidth; i++)
{
c = memoryFlag == 1 ? (char)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (char)Z80Ctrl->page[pnt+i] : (char)Z80Ctrl->iopage[pnt+i];
if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~'))
fputc((char)c, stdout);
else
fputc((char)' ', stdout);
}
printf("|\r\n");
fflush(stdout);
// Move on one row.
pnt += displayWidth;
addr += displayWidth;
// User abort (ESC), pause (Space) or all done?
//
keyIn = getch(0);
if(keyIn == ' ')
{
do {
keyIn = getch(0);
} while(keyIn != ' ' && keyIn != 0x1b);
}
// Escape key pressed, exit with 0 to indicate this to caller.
if (keyIn == 0x1b)
{
sleep(1);
result = 0;
goto memoryDumpExit;
}
// End of buffer, exit the loop.
if(pnt >= (memaddr + memsize))
{
break;
}
}
// Normal exit, return -1 to show no key pressed.
memoryDumpExit:
reset_terminal_mode();
return(result);
}
// Method to load a program or data file into the Z80 memory. First load into Virtual memory and then trigger a sync to bring Host RAM in line.
//
int z80load(int fdZ80, char *fileName)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
t_svcDirEnt mzfHeader;
// Pause the Z80.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Open the file and read directly into the Virtual memory via the share.
FILE *ptr;
ptr = fopen(fileName, "rb");
if(ptr)
{
printf("File:%s\n", fileName);
// First the header.
fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr);
printf("Load:%x\n", mzfHeader.loadAddr);
if(mzfHeader.loadAddr > 0x1000)
{
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
// Copy in the header.
memcpy((uint8_t *)&Z80Ctrl->memory[MZ_CMT_ADDR], (uint8_t *)&mzfHeader, MZF_HEADER_SIZE);
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
// Now read in the data.
fread(&Z80Ctrl->memory[mzfHeader.loadAddr], mzfHeader.fileSize, 1, ptr);
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
printf("Loaded %s, Size:%04x, Addr:%04x, Exec:%04x\n", fileName, mzfHeader.fileSize, mzfHeader.loadAddr, mzfHeader.execAddr);
}
// Sync the loaded image from Virtual memory to hard memory.
ioctlCmd.cmd = IOCTL_CMD_SYNC_TO_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Resume Z80 processing.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
else
printf("Couldnt open file\n");
return ret;
}
// Method to request basic Z80 operations.
//
int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long param3)
{
// Locals.
struct ioctlCmd ioctlCmd;
uint32_t idx;
int ret = 0;
switch(cmd)
{
case Z80_CMD_STOP:
// Use IOCTL to request Z80 to Stop (power off) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_START:
// Use IOCTL to request Z80 to Start (power on) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_PAUSE:
// Use IOCTL to request Z80 to pause processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_CONTINUE:
// Use IOCTL to request Z80 continue processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_RESET:
// Use IOCTL to request Z80 reset.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_SPEED:
// Check value is in range.
for(idx=1; idx < 256; idx+=idx)
{
if((uint32_t)param1 == idx) break;
}
if(idx == 256)
{
printf("Speed factor is illegal. It must be a multiple value of the original CPU clock, ie. 1x, 2x, 4x etc\n");
ret = -1;
} else
{
// Use IOCTL to request Z80 cpu freq change.
ioctlCmd.speed.speedMultiplier = (uint32_t)param1;
ioctlCmd.cmd = IOCTL_CMD_Z80_CPU_FREQ;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case CPLD_CMD_SEND_CMD:
// Build up the IOCTL command to request the given data is sent to the CPLD.
ioctlCmd.cmd = IOCTL_CMD_CPLD_CMD;
ioctlCmd.cpld.cmd = (uint32_t)param1;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_DUMP_MEMORY:
// If virtual memory, we can dump it via the shared memory segment.
if((uint8_t)param1)
{
memoryDump((uint32_t)param2, (uint32_t)param3, (uint8_t)param1, (uint8_t)param1 == 2 || (uint8_t)param1 == 3 ? 32 : 8, (uint32_t)param2, 0);
} else
{
// Build an IOCTL command to get the driver to dump the memory.
ioctlCmd.cmd = IOCTL_CMD_DUMP_MEMORY;
ioctlCmd.addr.start = (uint32_t)param2;
ioctlCmd.addr.end = (uint32_t)param2+(uint32_t)param3;
ioctlCmd.addr.size = (uint32_t)param3;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case Z80_CMD_HOST_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_VIRTUAL_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_VIRTUAL_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_MEMORY_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_Z80_MEMTEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_PRL_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_PRL_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_SPI_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_SPI_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
default:
printf("Command not supported!\n");
ret = -1;
break;
}
return ret;
}
// Method to perform some simple tests on the Z80 emulator.
//
int z80test(int fdZ80)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
// Stop the Z80.
//
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
FILE *ptr;
ptr = fopen("/customer/mz700.rom", "rb");
if(ptr)
{
fread(&Z80Ctrl->memory, 65536, 1, ptr);
} else printf("Couldnt open file\n");
// Configure the Z80.
//
printf("Send SETPC\n");
ioctlCmd.z80.pc = 0;
ioctl(fdZ80, IOCTL_CMD_SETPC, &ioctlCmd);
memoryDump(0 , 65536, 1, 8, 0, 0);
// Start the Z80.
//
printf("Send START\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(10);
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
memoryDump(0, 65536, 1, 8, 0, 0);
out:
return ret;
}
// Output usage screen. So mamy commands you do need to be prompted!!
void showArgs(char *progName, struct optparse *options)
{
printf("%s %s %s %s\n\n", progName, VERSION, COPYRIGHT, AUTHOR);
printf("Synopsis:\n");
printf("%s --help # This help screen.\n", progName);
printf(" --cmd <command> = RESET # Reset the Z80\n");
printf(" = STOP # Stop and power off the Z80\n");
printf(" = START # Power on and start the Z80\n");
printf(" = PAUSE # Pause running Z80\n");
printf(" = CONTINUE # Continue Z80 execution\n");
printf(" = HOSTRAM # Use HOST DRAM\n");
printf(" = VIRTRAM # Use Virtual RAM\n");
printf(" = SPEED --speed <1, 2, 4, 8, 16, 32, 64, 128> # In Virtual RAM mode, set CPU speed to base clock x factor.\n");
printf(" = LOADMZF --file <mzf filename> # Load MZF file into memory.\n");
printf(" = DUMP --start <24bit addr> --end <24bit addr> --virtual <0 - Host RAM, 1 = Virtual RAM, 2 = PageTable, 3 = IOPageTable>\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
printf(" = Z80TEST # Perform various debugging tests\n");
printf(" = SPITEST # Perform SPI testing\n");
printf(" = PRLTEST # Perform Parallel Bus testing\n");
printf(" = Z80MEMTEST # Perform HOST memory tests.\n");
}
int main(int argc, char *argv[])
{
int fdZ80;
char buff[64];
char cmd[64] = { 0 };
char fileName[256] = { 0 };
int opt;
long hexData = 0;
long speedMultiplier = 1;
long startAddr = 0x0000;
long endAddr = 0x1000;
int virtualMemory = 0;
int helpFlag = 0;
int verboseFlag = 0;
// Define parameters to be processed.
struct optparse options;
static struct optparse_long long_options[] =
{
{"help", 'h', OPTPARSE_NONE},
{"cmd", 'c', OPTPARSE_REQUIRED},
{"file", 'f', OPTPARSE_REQUIRED},
{"data", 'd', OPTPARSE_REQUIRED},
{"speed", 'S', OPTPARSE_REQUIRED},
{"virtual", 'V', OPTPARSE_REQUIRED},
{"start", 's', OPTPARSE_REQUIRED},
{"end", 'e', OPTPARSE_REQUIRED},
{"verbose", 'v', OPTPARSE_NONE},
{0}
};
// Parse the command line options.
//
optparse_init(&options, argv);
while((opt = optparse_long(&options, long_options, NULL)) != -1)
{
switch(opt)
{
// Hex data.
case 'd':
hexData = strtol(options.optarg, NULL, 0);
//printf("Hex data:%08x\n", hexData);
break;
// Start address for memory operations.
case 's':
startAddr = strtol(options.optarg, NULL, 0);
//printf("Start Addr:%04x\n", startAddr);
break;
// Speed multiplication factor for CPU governor when running in virtual memory.
case 'S':
speedMultiplier = strtol(options.optarg, NULL, 0);
//printf("Speed = base freq x %d\n", speedFactor);
break;
// End address for memory operations.
case 'e':
endAddr = strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Virtual memory flag, 0 = host, 1 = virtual memory, 2 = page table, 3 = iopage table.
case 'V':
virtualMemory = atoi(options.optarg);
break;
// Filename.
case 'f':
strcpy(fileName, options.optarg);
break;
// Command to execute.
case 'c':
strcpy(cmd, options.optarg);
break;
// Verbose mode.
case 'v':
verboseFlag = 1;
break;
// Command help needed.
case 'h':
helpFlag = 1;
showArgs(argv[0], &options);
break;
// Unrecognised, show synopsis.
case '?':
showArgs(argv[0], &options);
printf("%s: %s\n", argv[0], options.errmsg);
return(1);
}
}
// Open the z80drv driver and attach to its shared memory, basically the Z80 control structure which includes the virtual Z80 memory.
fdZ80 = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
if(fdZ80 >= 0)
{
Z80Ctrl = (t_Z80Ctrl *)mmap(0, sizeof(t_Z80Ctrl), PROT_READ | PROT_WRITE, MAP_SHARED, fdZ80, 0);
if(Z80Ctrl == (void *)-1)
{
printf("Failed to attach to the Z80 Control structure, cannot continue, exitting....\n");
close(fdZ80);
exit(1);
}
} else
{
printf("Failed to open the Z80 Driver, exitting...\n");
exit(1);
}
// Basic string to method mapping. Started off with just 1 or two but has grown, may need a table!
if(strcasecmp(cmd, "LOADMZF") == 0)
{
z80load(fdZ80, fileName);
} else
if(strcasecmp(cmd, "RESET") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_RESET, 0, 0, 0);
} else
if(strcasecmp(cmd, "STOP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_STOP, 0, 0, 0);
} else
if(strcasecmp(cmd, "START") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_START, 0, 0, 0);
} else
if(strcasecmp(cmd, "PAUSE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_PAUSE, 0, 0, 0);
} else
if(strcasecmp(cmd, "CONTINUE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_CONTINUE, 0, 0, 0);
} else
if(strcasecmp(cmd, "SPEED") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_SPEED, speedMultiplier, 0, 0);
} else
if(strcasecmp(cmd, "DUMP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_DUMP_MEMORY, virtualMemory, startAddr, (endAddr - startAddr));
} else
if(strcasecmp(cmd, "HOSTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_HOST_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "VIRTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_VIRTUAL_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "CPLDCMD") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SEND_CMD, hexData, 0, 0);
} else
// Test methods, if the code is built-in to the driver.
if(strcasecmp(cmd, "Z80TEST") == 0)
{
z80test(fdZ80);
} else
if(strcasecmp(cmd, "SPITEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SPI_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "PRLTEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_PRL_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "Z80MEMTEST") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_MEMORY_TEST, 0, 0, 0);
}
else
{
showArgs(argv[0], &options);
printf("No command given, nothing done!\n");
}
// Unmap shared memory and close the device.
munmap(Z80Ctrl, sizeof(t_Z80Ctrl));
close(fdZ80);
return(0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,284 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80driver.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Driver
// This file contains the declarations used in the z80drv device driver.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of this driver and in all ways, is compatible with
// the original Z80.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80DRIVER_H
#define Z80DRIVER_H
// Constants.
#define Z80_VIRTUAL_ROM_SIZE 16384 // Sized to maximum ROM which is the MZ-800 ROM.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 8) // (PAGE_SIZE * 2) // max size mmaped to userspace
#define Z80_VIRTUAL_MEMORY_SIZE Z80_VIRTUAL_RAM_SIZE + Z80_VIRTUAL_ROM_SIZE
#define Z80_MEMORY_PAGE_SIZE 16
#define MAX_SCREEN_WIDTH 132
#define DEVICE_NAME "z80drv"
#define CLASS_NAME "mogu"
// Memory and IO page types. Used to create a memory page which maps type of address space to real address space on host or virtual memory.
#define MEMORY_TYPE_VIRTUAL_MASK 0x00FFFFFF
#define MEMORY_TYPE_REAL_MASK 0x0000FFFF
#define IO_TYPE_MASK 0x0000FFFF
#define MEMORY_TYPE_INHIBIT 0x00000000
#define MEMORY_TYPE_PHYSICAL_RAM 0x80000000
#define MEMORY_TYPE_PHYSICAL_ROM 0x40000000
#define MEMORY_TYPE_PHYSICAL_VRAM 0x20000000
#define MEMORY_TYPE_PHYSICAL_HW 0x10000000
#define MEMORY_TYPE_VIRTUAL_RAM 0x08000000
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_HW 0x02000000
#define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000
// Approximate governor delays to regulate emulated CPU speed.
#define MZ700_INSTRUCTION_DELAY_3_54MHZ 253
#define MZ700_INSTRUCTION_DELAY_7MHZ 126
#define MZ700_INSTRUCTION_DELAY_14MHZ 63
#define MZ700_INSTRUCTION_DELAY_28MHZ 32
#define MZ700_INSTRUCTION_DELAY_56MHZ 16
#define MZ700_INSTRUCTION_DELAY_112MHZ 8
#define MZ700_INSTRUCTION_DELAY_224MHZ 4
#define MZ700_INSTRUCTION_DELAY_448MHZ 1
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S'
#define IOCTL_CMD_Z80_PAUSE 'P'
#define IOCTL_CMD_Z80_RESET 'R'
#define IOCTL_CMD_Z80_CONTINUE 'C'
#define IOCTL_CMD_USE_HOST_RAM 'x'
#define IOCTL_CMD_USE_VIRTUAL_RAM 'X'
#define IOCTL_CMD_DUMP_MEMORY 'M'
#define IOCTL_CMD_Z80_CPU_FREQ 'F'
#define IOCTL_CMD_CPLD_CMD 'z'
#define IOCTL_CMD_SEND _IOW('c', 'c', int32_t *)
#define IOCTL_CMD_SETPC _IOW('p', 'p', int32_t *)
#define IOCTL_CMD_SYNC_TO_HOST_RAM 'V'
#define IOCTL_CMD_SPI_TEST '1'
#define IOCTL_CMD_PRL_TEST '2'
#define IOCTL_CMD_Z80_MEMTEST '3'
// Chip Select map MZ80K-MZ700.
//
// 0000 - 0FFF = CS_ROMni : R/W : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF = CS_RAMni : R/W : MZ80K/A/700 = RAM
// C000 - CFFF = CS_ROMni : R/W : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF = CS_VRAMni : R/W : MZ80K/A/700 = VRAM
// D800 - DFFF = CS_VRAMni : R/W : MZ700 = Colour VRAM (MZ700)
// E000 - E003 = CS_8255n : R/W : MZ80K/A/700 = 8255
// E004 - E007 = CS_8254n : R/W : MZ80K/A/700 = 8254
// E008 - E00B = CS_LS367n : R/W : MZ80K/A/700 = LS367
// E00C - E00F = CS_ESWPn : R : MZ80A = Memory Swap (MZ80A)
// E010 - E013 = CS_ESWPn : R : MZ80A = Reset Memory Swap (MZ80A)
// E014 = CS_E5n : R/W : MZ80A/700 = Normal CRT display (in Video Controller)
// E015 = CS_E6n : R/W : MZ80A/700 = Reverse CRT display (in Video Controller)
// E200 - E2FF = : R/W : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF = : R/W : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF = : R/W : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF = : R/W : MZ80K/A/700 = Floppy Disk interface.
//
// Chip Select map MZ800
//
// FC - FF = CS_PIOn : R/W : MZ800/MZ1500 = Z80 PIO Printer Interface
// F2 = CS_PSG0n : W : MZ800/MZ1500 = Programable Sound Generator, MZ-800 = Mono, MZ-1500 = Left Channel
// F3 = CS_PSG1n : W : MZ1500 = Programable Sound Generator, MZ-1500 = Right Channel
// E9 = CS_PSG(X)n: W : MZ1500 = Simultaneous write to both PSG's.
// F0 - F1 = CS_JOYSTK : R : MZ800 = Joystick 1 and 2
// CC = CS_GWF : W : MZ800 = CRTC GWF Write format Register
// CD = CS_GRF : W : MZ800 = CRTC GRF Read format Register
// CE = CS_GDMD : W : MZ800 = CRTC GDMD Mode Register
// CF = CS_GCRTC : W : MZ800 = CRTC GCRTC Control Register
// D4 - D7 = CS
// D000 - DFFF
// MZ700/MZ800 memory mode switch?
//
// MZ-700 MZ-800
// |0000:0FFF|1000:1FFF|1000:CFFF|C000:CFFF|D000:FFFF |0000:7FFF|1000:1FFF|2000:7FFF|8000:BFFF|C000:CFFF|C000:DFFF|E000:FFFF
// -------------------------------------------------- ----------------------------------------------------------------------
// OUT 0xE0 = |DRAM | | | | |DRAM | | | | | |
// OUT 0xE1 = | | | | |DRAM | | | | | | |DRAM
// OUT 0xE2 = |MONITOR | | | | |MONITOR | | | | | |
// OUT 0xE3 = | | | | |Memory Mapped I/O | | | | | | |Upper MONITOR ROM
// OUT 0xE4 = |MONITOR | |DRAM | |Memory Mapped I/O |MONITOR |CGROM |DRAM |VRAM | |DRAM |Upper MONITOR ROM
// OUT 0xE5 = | | | | |Inhibit | | | | | | |Inhibit
// OUT 0xE6 = | | | | |<return> | | | | | | |<return>
// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | |
// IN 0xE1 = | |DRAM | |DRAM | | |<return> | |DRAM | | |
//
// <return> = Return to the state prior to the complimentary command being invoked.
// * = MZ-800 host only.
// Macros to lookup and test to see if a given memory block or IO byte is of a given type. Also macros to read/write to the memory block and IO byte.
#define MEMORY_BLOCK_GRANULARITY 0x800
#define MEMORY_BLOCK_SLOTS (0x10000 / MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_MASK (0x10000 - MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_SHIFT 11
#define getPageData(a) (Z80Ctrl->page[(a & 0xF800) >> MEMORY_BLOCK_SHIFT])
#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF])
#define getPageType(a, mask) (getPageData(a) & mask)
#define getPageAddr(a, mask) ((getPageData(a) & mask) + (a & (MEMORY_BLOCK_GRANULARITY-1)))
#define getIOPageType(a, mask) (getIOPageData(a) & mask)
#define getIOPageAddr(a, mask) (getIOPageData(a) & mask)
#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)])
#define realPort(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_MASK)
#define isPhysicalRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_RAM))
#define isPhysicalVRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_VRAM))
#define isPhysicalROM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_ROM))
#define isPhysicalMemory(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))])
#define isPhysicalHW(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_HW))
#define isPhysical(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM)))
#define isPhysicalIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_PHYSICAL_HW)
#define isVirtualRAM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM))
#define isVirtualROM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_ROM))
#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM)))
#define isVirtualHW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_HW))
#define isVirtualIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_VIRTUAL_HW)
#define isHW(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_VIRTUAL_HW)))
#define readVirtualRAM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ])
#define readVirtualROM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE ])
#define writeVirtualRAM(a, d) { Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; }
#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; }
#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = Z80Ctrl->page[_block_]; }
#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; }
#define IO_ADDR_E0 0xE0
#define IO_ADDR_E1 0xE1
#define IO_ADDR_E2 0xE2
#define IO_ADDR_E3 0xE3
#define IO_ADDR_E4 0xE4
#define IO_ADDR_E5 0xE5
#define IO_ADDR_E6 0xE6
#define IO_ADDR_E7 0xE7
enum Z80_RUN_STATES {
Z80_STOP = 0x00,
Z80_STOPPED = 0x01,
Z80_PAUSE = 0x02,
Z80_PAUSED = 0x03,
Z80_CONTINUE = 0x04,
Z80_RUNNING = 0x05,
};
enum Z80_MEMORY_PROFILE {
USE_PHYSICAL_RAM = 0x00,
USE_VIRTUAL_RAM = 0x01
};
typedef struct {
// Main memory, linear but indexed as though it were banks in 1K pages.
uint8_t memory[Z80_VIRTUAL_MEMORY_SIZE];
// Page pointer map.
//
// Each pointer points to a byte or block of bytes in the Z80 Memory frame, 64K Real + Banked.
// This is currently set at a block of size 0x800 per memory pointer for the MZ-700.
// The LSB of the pointer is a direct memory index to a byte or block of bytes, the upper byte of the pointer indicates type of memory space.
// 0x80<FFFFFF> - physical host RAM
// 0x40<FFFFFF> - physical host ROM
// 0x20<FFFFFF> - physical host VRAM
// 0x10<FFFFFF> - physical host hardware
// 0x08<FFFFFF> - virtual host RAM
// 0x04<FFFFFF> - virtual host ROM
// 0x02<FFFFFF> - virtual host hardware
// 16bit Input Address -> map -> Pointer to 24bit memory address + type flag.
// -> Pointer+<low bits of address> to 24bit memory address + type flag.
uint32_t page[MEMORY_BLOCK_SLOTS];
uint32_t shadowPage[MEMORY_BLOCK_SLOTS];
// I/O Page map.
//
// This is a map to indicate the use of the I/O page and allow any required remapping.
// <0x80>FF<I/O Address> - physical host hardware
// <0x40>FF<I/O Address> - virtual host hardware
// 16bit Input Address -> map -> Actual 16bit address to use + type flag.
uint32_t iopage[65536];
// Default page mode configured. This value reflects the default page and iotable map.
uint8_t defaultPageMode;
// Refresh DRAM mode. 1 = Refresh, 0 = No refresh. Only applicable when running code in virtual Kernel RAM.
uint8_t refreshDRAM;
// Inhibit mode is where certain memory ranges are inhibitted. The memory page is set to inhibit and this flag
// blocks actions which arent allowed during inhibit.
uint8_t inhibitMode;
// Address caching. Used to minimise instruction length sent to CPLD.
uint16_t z80PrevAddr;
uint16_t z80PrevPort;
// Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe;
uint8_t keyportShiftCtrl;
uint8_t keyportHotKey;
// Governor is the delay in a 32bit loop per Z80 opcode, used to govern execution speed when using virtual memory.
// This mechanism will eventually be tied into the M/T-state calculation for a more precise delay, but at the moment,
// with the Z80 assigned to an isolated CPU, it allows time sensitive tasks such as the tape recorder to work.
// The lower the value the faster the CPU speed.
uint32_t cpuGovernorDelay;
} t_Z80Ctrl;
// IOCTL structure for passing data from user space to driver to perform commands.
//
struct z80_addr {
uint32_t start;
uint32_t end;
uint32_t size;
};
struct z80_ctrl {
uint16_t pc;
};
struct speed {
uint32_t speedMultiplier;
};
struct cpld_ctrl {
uint32_t cmd;
};
struct ioctlCmd {
int32_t cmd;
union {
struct z80_addr addr;
struct z80_ctrl z80;
struct speed speed;
struct cpld_ctrl cpld;
};
};
// Prototypes.
void setupMemory(enum Z80_MEMORY_PROFILE mode);
#endif

View File

@@ -1,428 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the methods used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
//-------------------------------------------------------------------------------------------------------------------------------
//
// User space driver access.
//
//-------------------------------------------------------------------------------------------------------------------------------
// Initialise the SOM hardware used to communicate with the z80 socket and host hardware.
// The SOM interfaces to a CPLD which provides voltage level translation and also encapsulates the Z80 timing cycles as recreating
// them within the SOM is much more tricky.
//
// As this is an embedded device and performance/latency are priorities, minimal structured code is used to keep call stack and
// generated code to a mimimum without relying on the optimiser.
int z80io_init(void)
{
// Locals.
int ret = 0;
// Initialise GPIO. We call the HAL api to minimise time but for actual bit set/reset and read we go directly to registers to save time, increase throughput and minimise latency.
// Initialise the HAL.
MHal_GPIO_Init();
// Set the pads as GPIO devices. The HAL takes care of allocating and deallocating the padmux resources.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_0); // Word (16bit) bidirectional bus. Default is read with data set.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_HIGH_BYTE);
//MHal_GPIO_Pad_Set(PAD_GPIO8); // SPIO 4wire control lines setup by the spidev driver but controlled directly in this driver.
//MHal_GPIO_Pad_Set(PAD_GPIO9);
//MHal_GPIO_Pad_Set(PAD_GPIO10);
//MHal_GPIO_Pad_Set(PAD_GPIO11);
MHal_GPIO_Pad_Set(PAD_Z80IO_READY);
MHal_GPIO_Pad_Set(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Set(PAD_Z80IO_INT);
MHal_GPIO_Pad_Set(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Set(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Set(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Set(PAD_Z80IO_RSV1);
#ifdef NOTNEEDED
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_WRITE);
#endif
// Set required input pads.
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_0);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Odn(PAD_Z80IO_READY);
MHal_GPIO_Pad_Odn(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Odn(PAD_Z80IO_INT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Odn(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RSV1);
// Set required output pads.
#ifdef NOTNEEDED
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Oen(PAD_Z80IO_WRITE);
MHal_GPIO_Pull_High(PAD_Z80IO_WRITE);
#endif
// Control signals.
MHal_GPIO_Pad_Oen(PAD_Z80IO_HIGH_BYTE);
MHal_GPIO_Pull_High(PAD_Z80IO_HIGH_BYTE);
// Setup the MSPI0 device.
//
// Setup control, interrupts are not used.
MSPI_WRITE(MSPI_CTRL_OFFSET, MSPI_CPU_CLOCK_1_2 | MSPI_CTRL_CPOL_LOW | MSPI_CTRL_CPHA_HIGH | MSPI_CTRL_RESET | MSPI_CTRL_ENABLE_SPI);
// Setup LSB First mode.
MSPI_WRITE(MSPI_LSB_FIRST_OFFSET, 0x0);
// Setup clock.
CLK_WRITE(MSPI0_CLK_CFG, 0x1100)
// Setup the frame size (all buffers to 8bits).
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET+1, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET+1, 0xfff);
// Setup Chip Selects to inactive.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Switch Video and Audio to host.
z80io_SPI_Send16(0x00f0, NULL);
return ret;
}
//--------------------------------------------------------
// Parallel bus Methods.
//--------------------------------------------------------
// Methods to read data from the parallel bus.
// The CPLD returns status and Z80 data on the 8bit bus as it is marginally quicker than retrieving it over the SPI bus.
//
inline uint8_t z80io_PRL_Read8(uint8_t dataFlag)
{
// Locals.
uint8_t result = 0;
// Byte according to flag.
if(dataFlag)
SET_CPLD_READ_DATA()
else
SET_CPLD_READ_STATUS()
// Read the input registers and set value accordingly.
result = READ_CPLD_DATA_IN();
// Return 16bit value read from CPLD.
return(result);
}
inline uint16_t z80io_PRL_Read16(void)
{
// Locals.
uint16_t result = 0;
// Low byte first.
CLEAR_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result = (uint16_t)READ_CPLD_DATA_IN();
// High byte next.
SET_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result |= (uint16_t)(READ_CPLD_DATA_IN() << 8);
// Return 16bit value read from CPLD.
return(result);
}
// Parallel Bus methods were tried and tested but due to the GPIO bits being controlled by individual registers per bit, the setup time was longer
// than the transmission time of SPI. These methods are thus deprecated and a fusion of SPI and 8bit parallel is now used.
#ifdef NOTNEEDED
inline uint8_t z80io_PRL_Send8(uint8_t txData)
{
// Locals.
//
// Low byte only.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
inline uint8_t z80io_PRL_Send16(uint16_t txData)
{
// Locals.
//
// Low byte first.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
// High byte next.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
// Setup high byte.
if(txData & 0x8000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x4000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x2000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x1000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0800) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0400) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0200) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0100) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
#endif
//--------------------------------------------------------
// SPI Methods.
//--------------------------------------------------------
// Methods to send 8,16 or 32 bits. Each method is seperate to minimise logic and execution time, 8bit being most sensitive.
// Macros have also been defined for inline inclusion which dont read back the response data.
//
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint8_t)MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)(txData >> 16));
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint32_t)(MSPI_READ(MSPI_FULL_DEPLUX_RD00) | (MSPI_READ(MSPI_FULL_DEPLUX_RD02) << 16));
// Done.
return(timeout == 0);
}
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)
{
pr_info("Z80 Test Memory functionality not built-in.\n");
return(0);
}
uint8_t z80io_SPI_Test(void)
{
pr_info("SPI Test functionality not built-in.\n");
return(0);
}
uint8_t z80io_PRL_Test(void)
{
pr_info("Parallel Bus Test functionality not built-in.\n");
return(0);
}
#endif

View File

@@ -1,483 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the declarations used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80IO_H
#define Z80IO_H
#ifdef __cplusplus
extern "C" {
#endif
// Definitions to control compilation.
#define INCLUDE_TEST_METHODS 1
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
#define CPLD_CMD_FETCH_ADDR_P1 0x11
#define CPLD_CMD_FETCH_ADDR_P2 0x12
#define CPLD_CMD_FETCH_ADDR_P3 0x13
#define CPLD_CMD_FETCH_ADDR_P4 0x14
#define CPLD_CMD_FETCH_ADDR_P5 0x15
#define CPLD_CMD_FETCH_ADDR_P6 0x16
#define CPLD_CMD_FETCH_ADDR_P7 0x17
#define CPLD_CMD_WRITE_ADDR 0x18
#define CPLD_CMD_WRITE_ADDR_P1 0x19
#define CPLD_CMD_WRITE_ADDR_P2 0x1A
#define CPLD_CMD_WRITE_ADDR_P3 0x1B
#define CPLD_CMD_WRITE_ADDR_P4 0x1C
#define CPLD_CMD_WRITE_ADDR_P5 0x1D
#define CPLD_CMD_WRITE_ADDR_P6 0x1E
#define CPLD_CMD_WRITE_ADDR_P7 0x1F
#define CPLD_CMD_READ_ADDR 0x20
#define CPLD_CMD_READ_ADDR_P1 0x21
#define CPLD_CMD_READ_ADDR_P2 0x22
#define CPLD_CMD_READ_ADDR_P3 0x23
#define CPLD_CMD_READ_ADDR_P4 0x24
#define CPLD_CMD_READ_ADDR_P5 0x25
#define CPLD_CMD_READ_ADDR_P6 0x26
#define CPLD_CMD_READ_ADDR_P7 0x27
#define CPLD_CMD_WRITEIO_ADDR 0x28
#define CPLD_CMD_WRITEIO_ADDR_P1 0x29
#define CPLD_CMD_WRITEIO_ADDR_P2 0x2A
#define CPLD_CMD_WRITEIO_ADDR_P3 0x2B
#define CPLD_CMD_WRITEIO_ADDR_P4 0x2C
#define CPLD_CMD_WRITEIO_ADDR_P5 0x2D
#define CPLD_CMD_WRITEIO_ADDR_P6 0x2E
#define CPLD_CMD_WRITEIO_ADDR_P7 0x2F
#define CPLD_CMD_READIO_ADDR 0x30
#define CPLD_CMD_READIO_ADDR_P1 0x31
#define CPLD_CMD_READIO_ADDR_P2 0x32
#define CPLD_CMD_READIO_ADDR_P3 0x33
#define CPLD_CMD_READIO_ADDR_P4 0x34
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
#define CPLD_CMD_SET_AUTO_REFRESH 0xF1
#define CPLD_CMD_CLEAR_AUTO_REFRESH 0xF2
#define CPLD_CMD_SET_SPI_LOOPBACK 0xFE
#define CPLD_CMD_NOP1 0x00
#define CPLD_CMD_NOP2 0xFF
// Pad numbers for using the MHal GPIO library.
#define PAD_Z80IO_IN_DATA_0 PAD_GPIO0
#define PAD_Z80IO_IN_DATA_1 PAD_GPIO1
#define PAD_Z80IO_IN_DATA_2 PAD_GPIO2
#define PAD_Z80IO_IN_DATA_3 PAD_GPIO3
#define PAD_Z80IO_IN_DATA_4 PAD_GPIO4
#define PAD_Z80IO_IN_DATA_5 PAD_GPIO5
#define PAD_Z80IO_IN_DATA_6 PAD_GPIO6
#define PAD_Z80IO_IN_DATA_7 PAD_GPIO7
#define PAD_SPIO_0 PAD_GPIO8
#define PAD_SPIO_1 PAD_GPIO9
#define PAD_SPIO_2 PAD_GPIO10
#define PAD_SPIO_3 PAD_GPIO11
#define PAD_Z80IO_HIGH_BYTE PAD_SAR_GPIO2 // Byte requiured, 0 = Low Byte, 1 = High Byte.
#define PAD_Z80IO_READY PAD_GPIO12
#define PAD_Z80IO_LTSTATE PAD_PM_IRIN // IRIN
#define PAD_Z80IO_BUSRQ PAD_GPIO13
#define PAD_Z80IO_BUSACK PAD_GPIO14
#define PAD_Z80IO_INT PAD_UART0_RX // GPIO47
#define PAD_Z80IO_NMI PAD_UART0_TX // GPIO48
#define PAD_Z80IO_WAIT PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_RESET PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_RSV1 PAD_SATA_GPIO // GPIO90
// Physical register addresses.
#define PAD_Z80IO_IN_DATA_0_ADDR 0x103C00
#define PAD_Z80IO_IN_DATA_1_ADDR 0x103C02
#define PAD_Z80IO_IN_DATA_2_ADDR 0x103C04
#define PAD_Z80IO_IN_DATA_3_ADDR 0x103C06
#define PAD_Z80IO_IN_DATA_4_ADDR 0x103C08
#define PAD_Z80IO_IN_DATA_5_ADDR 0x103C0A
#define PAD_Z80IO_IN_DATA_6_ADDR 0x103C0C
#define PAD_Z80IO_IN_DATA_7_ADDR 0x103C0E
#define PAD_SPIO_0_ADDR 0x103C10
#define PAD_SPIO_1_ADDR 0x103C12
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_LTSTATE_ADDR 0xF28 // IRIN
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
#define PAD_Z80IO_INT_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_NMI_ADDR 0x103C32 // GPIO48
#define PAD_Z80IO_WAIT_ADDR 0x103C80 // GPIO85
#define PAD_Z80IO_RESET_ADDR 0x103C82 // GPIO86
#define PAD_Z80IO_RSV1_ADDR 0x103C8A // GPIO90
#ifdef NOTNEEDED
#define PAD_Z80IO_OUT_DATA_0 PAD_GPIO12
#define PAD_Z80IO_OUT_DATA_1 PAD_GPIO13
#define PAD_Z80IO_OUT_DATA_2 PAD_GPIO14
#define PAD_Z80IO_OUT_DATA_3 PAD_UART0_RX // GPIO47
#define PAD_Z80IO_OUT_DATA_4 PAD_UART0_TX // GPIO48
#define PAD_Z80IO_OUT_DATA_5 PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_OUT_DATA_6 PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_OUT_DATA_7 PAD_SATA_GPIO // GPIO90
#define PAD_Z80IO_WRITE PAD_PM_IRIN // Write data clock.
#endif
//-------------------------------------------------------------------------------------------------
// The definitions below come from SigmaStar kernel drivers. No header file exists hence the
// duplication.
//-------------------------------------------------------------------------------------------------
#define SUPPORT_SPI_1 0
#define MAX_SUPPORT_BITS 16
#define BANK_TO_ADDR32(b) (b<<9)
#define BANK_SIZE 0x200
#define MS_BASE_REG_RIU_PA 0x1F000000
#define gChipBaseAddr 0xFD203C00
#define gPmSleepBaseAddr 0xFD001C00
#define gSarBaseAddr 0xFD002800
#define gRIUBaseAddr 0xFD000000
#define gMOVDMAAddr 0xFD201600
#define gClkBaseAddr 0xFD207000
#define gMspBaseAddr 0xfd222000
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MSPI0_BANK_ADDR 0x1110
#define MSPI1_BANK_ADDR 0x1111
#define CLK__BANK_ADDR 0x1038
#define CHIPTOP_BANK_ADDR 0x101E
#define MOVDMA_BANK_ADDR 0x100B
#define BASE_REG_MSPI0_ADDR MSPI0_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111000)
#define BASE_REG_MSPI1_ADDR MSPI1_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111100)
#define BASE_REG_CLK_ADDR CLK__BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x103800)
#define BASE_REG_CHIPTOP_ADDR CHIPTOP_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x101E00)
//-------------------------------------------------------------------------------------------------
// Hardware Register Capability
//-------------------------------------------------------------------------------------------------
#define MSPI_WRITE_BUF_OFFSET 0x40
#define MSPI_READ_BUF_OFFSET 0x44
#define MSPI_WBF_SIZE_OFFSET 0x48
#define MSPI_RBF_SIZE_OFFSET 0x48
// read/ write buffer size
#define MSPI_RWSIZE_MASK 0xFF
#define MSPI_RSIZE_BIT_OFFSET 0x8
#define MAX_READ_BUF_SIZE 0x8
#define MAX_WRITE_BUF_SIZE 0x8
// CLK config
#define MSPI_CTRL_OFFSET 0x49
#define MSPI_CLK_CLOCK_OFFSET 0x49
#define MSPI_CLK_CLOCK_BIT_OFFSET 0x08
#define MSPI_CLK_CLOCK_MASK 0xFF
#define MSPI_CLK_PHASE_MASK 0x40
#define MSPI_CLK_PHASE_BIT_OFFSET 0x06
#define MSPI_CLK_POLARITY_MASK 0x80
#define MSPI_CLK_POLARITY_BIT_OFFSET 0x07
#define MSPI_CLK_PHASE_MAX 0x1
#define MSPI_CLK_POLARITY_MAX 0x1
#define MSPI_CLK_CLOCK_MAX 0x7
#define MSPI_CTRL_CPOL_LOW 0x00
#define MSPI_CTRL_CPOL_HIGH 0x80
#define MSPI_CTRL_CPHA_LOW 0x00
#define MSPI_CTRL_CPHA_HIGH 0x40
#define MSPI_CTRL_3WIRE 0x10
#define MSPI_CTRL_INTEN 0x04
#define MSPI_CTRL_RESET 0x02
#define MSPI_CTRL_ENABLE_SPI 0x01
// DC config
#define MSPI_DC_MASK 0xFF
#define MSPI_DC_BIT_OFFSET 0x08
#define MSPI_DC_TR_START_OFFSET 0x4A
#define MSPI_DC_TRSTART_MAX 0xFF
#define MSPI_DC_TR_END_OFFSET 0x4A
#define MSPI_DC_TREND_MAX 0xFF
#define MSPI_DC_TB_OFFSET 0x4B
#define MSPI_DC_TB_MAX 0xFF
#define MSPI_DC_TRW_OFFSET 0x4B
#define MSPI_DC_TRW_MAX 0xFF
// Frame Config
#define MSPI_FRAME_WBIT_OFFSET 0x4C
#define MSPI_FRAME_RBIT_OFFSET 0x4E
#define MSPI_FRAME_BIT_MAX 0x07
#define MSPI_FRAME_BIT_MASK 0x07
#define MSPI_FRAME_BIT_FIELD 0x03
#define MSPI_LSB_FIRST_OFFSET 0x50
#define MSPI_TRIGGER_OFFSET 0x5A
#define MSPI_DONE_OFFSET 0x5B
#define MSPI_DONE_CLEAR_OFFSET 0x5C
#define MSPI_CHIP_SELECT_OFFSET 0x5F
#define MSPI_CS1_DISABLE 0x01
#define MSPI_CS1_ENABLE 0x00
#define MSPI_CS2_DISABLE 0x02
#define MSPI_CS2_ENABLE 0x00
#define MSPI_CS3_DISABLE 0x04
#define MSPI_CS3_ENABLE 0x00
#define MSPI_CS4_DISABLE 0x08
#define MSPI_CS4_ENABLE 0x00
#define MSPI_CS5_DISABLE 0x10
#define MSPI_CS5_ENABLE 0x00
#define MSPI_CS6_DISABLE 0x20
#define MSPI_CS6_ENABLE 0x00
#define MSPI_CS7_DISABLE 0x40
#define MSPI_CS7_ENABLE 0x00
#define MSPI_CS8_DISABLE 0x80
#define MSPI_CS8_ENABLE 0x00
#define MSPI_FULL_DEPLUX_RD_CNT (0x77)
#define MSPI_FULL_DEPLUX_RD00 (0x78)
#define MSPI_FULL_DEPLUX_RD01 (0x78)
#define MSPI_FULL_DEPLUX_RD02 (0x79)
#define MSPI_FULL_DEPLUX_RD03 (0x79)
#define MSPI_FULL_DEPLUX_RD04 (0x7a)
#define MSPI_FULL_DEPLUX_RD05 (0x7a)
#define MSPI_FULL_DEPLUX_RD06 (0x7b)
#define MSPI_FULL_DEPLUX_RD07 (0x7b)
#define MSPI_FULL_DEPLUX_RD08 (0x7c)
#define MSPI_FULL_DEPLUX_RD09 (0x7c)
#define MSPI_FULL_DEPLUX_RD10 (0x7d)
#define MSPI_FULL_DEPLUX_RD11 (0x7d)
#define MSPI_FULL_DEPLUX_RD12 (0x7e)
#define MSPI_FULL_DEPLUX_RD13 (0x7e)
#define MSPI_FULL_DEPLUX_RD14 (0x7f)
#define MSPI_FULL_DEPLUX_RD15 (0x7f)
//chip select bit map
#define MSPI_CHIP_SELECT_MAX 0x07
// control bit
#define MSPI_DONE_FLAG 0x01
#define MSPI_TRIGGER 0x01
#define MSPI_CLEAR_DONE 0x01
#define MSPI_INT_ENABLE 0x04
#define MSPI_RESET 0x02
#define MSPI_ENABLE 0x01
// clk_mspi0
#define MSPI0_CLK_CFG 0x33 //bit 2 ~bit 3
#define MSPI0_CLK_108M 0x00
#define MSPI0_CLK_54M 0x04
#define MSPI0_CLK_12M 0x08
#define MSPI0_CLK_MASK 0x0F
// clk_mspi1
#define MSPI1_CLK_CFG 0x33 //bit 10 ~bit 11
#define MSPI1_CLK_108M 0x0000
#define MSPI1_CLK_54M 0x0400
#define MSPI1_CLK_12M 0x0800
#define MSPI1_CLK_MASK 0x0F00
// clk_mspi
#define MSPI_CLK_CFG 0x33
#define MSPI_SELECT_0 0x0000
#define MSPI_SELECT_1 0x4000
#define MSPI_CLK_MASK 0xF000
// Clock settings
#define MSPI_CPU_CLOCK_1_2 0x0000
#define MSPI_CPU_CLOCK_1_4 0x0100
#define MSPI_CPU_CLOCK_1_8 0x0200
#define MSPI_CPU_CLOCK_1_16 0x0300
#define MSPI_CPU_CLOCK_1_32 0x0400
#define MSPI_CPU_CLOCK_1_64 0x0500
#define MSPI_CPU_CLOCK_1_128 0x0600
#define MSPI_CPU_CLOCK_1_256 0x0700
//CHITOP 101E mspi mode select
#define MSPI0_MODE 0x0C //bit0~bit1
#define MSPI0_MODE_MASK 0x07
#define MSPI1_MODE 0x0C //bit4~bit5
#define MSPI1_MODE_MASK 0x70
#define EJTAG_MODE 0xF
#define EJTAG_MODE_1 0x01
#define EJTAG_MODE_2 0x02
#define EJTAG_MODE_3 0x03
#define EJTAG_MODE_MASK 0x03
//MOVDMA 100B
#define MOV_DMA_SRC_ADDR_L 0x03
#define MOV_DMA_SRC_ADDR_H 0x04
#define MOV_DMA_DST_ADDR_L 0x05
#define MOV_DMA_DST_ADDR_H 0x06
#define MOV_DMA_BYTE_CNT_L 0x07
#define MOV_DMA_BYTE_CNT_H 0x08
#define DMA_MOVE0_IRQ_CLR 0x28
#define MOV_DMA_IRQ_FINAL_STATUS 0x2A
#define DMA_MOVE0_ENABLE 0x00
#define DMA_RW 0x50 //0 for dma write to device, 1 for dma read from device
#define DMA_READ 0x01
#define DMA_WRITE 0x00
#define DMA_DEVICE_MODE 0x51
#define DMA_DEVICE_SEL 0x52
//spi dma
#define MSPI_DMA_DATA_LENGTH_L 0x30
#define MSPI_DMA_DATA_LENGTH_H 0x31
#define MSPI_DMA_ENABLE 0x32
#define MSPI_DMA_RW_MODE 0x33
#define MSPI_DMA_WRITE 0x00
#define MSPI_DMA_READ 0x01
#define MSTAR_SPI_TIMEOUT_MS 30000
#define MSTAR_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA /*| SPI_CS_HIGH | SPI_NO_CS | SPI_LSB_FIRST*/)
//-------------------------------------------------------------------------------------------------
// Macros
//-------------------------------------------------------------------------------------------------
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define READ_BYTE(_reg) (*(volatile u8*)(_reg))
#define READ_WORD(_reg) (*(volatile u16*)(_reg))
#define READ_LONG(_reg) (*(volatile u32*)(_reg))
#define WRITE_BYTE(_reg, _val) {(*((volatile u8*)(_reg))) = (u8)(_val); }
#define WRITE_WORD(_reg, _val) {(*((volatile u16*)(_reg))) = (u16)(_val); }
#define WRITE_LONG(_reg, _val) {(*((volatile u32*)(_reg))) = (u32)(_val); }
#define WRITE_WORD_MASK(_reg, _val, _mask) {(*((volatile u16*)(_reg))) = ((*((volatile u16*)(_reg))) & ~(_mask)) | ((u16)(_val) & (_mask)); }
#define READ_CPLD_DATA_IN() ((MHal_RIU_REG(PAD_Z80IO_IN_DATA_7_ADDR) & 0x1) << 7 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_6_ADDR) & 0x1) << 6 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_5_ADDR) & 0x1) << 5 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_4_ADDR) & 0x1) << 4 |\
(MHal_RIU_REG(PAD_Z80IO_IN_DATA_3_ADDR) & 0x1) << 3 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_2_ADDR) & 0x1) << 2 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_1_ADDR) & 0x1) << 1 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_0_ADDR) & 0x1))
#define SET_CPLD_READ_DATA() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define SET_CPLD_READ_STATUS() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define SET_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define CLEAR_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define CPLD_READY() (MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1)
#define CPLD_RESET() (MHal_RIU_REG(PAD_Z80IO_RESET_ADDR) & 0x1)
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte
//#define MSPI_WRITE(_reg_, _val_) {pr_info("PDS: MSPI_WRITE(0x%x, 0x%x, 0x%x)\n", _reg_, _val_, gMspBaseAddr + ((_reg_)<<2)); WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_)); }
#define MSPI_WRITE(_reg_, _val_) WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_));
//write 2 byte mask
//#define MSPI_WRITE_MASK(_reg_, _val_, mask) {pr_info("PDS: WRITE_LONG(0x%x, 0x%x, mask=0x%x)\n", _reg_, _val_, mask); WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask)); }
#define MSPI_WRITE_MASK(_reg_, _val_, mask) WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask));
#define CLK_READ(_reg_) READ_WORD(gClkBaseAddr + ((_reg_)<<2))
//#define CLK_WRITE(_reg_, _val_) {pr_info("PDS: CLK_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_)); }
#define CLK_WRITE(_reg_, _val_) WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_));
#define CHIPTOP_READ(_reg_) READ_WORD(gChipBaseAddr + ((_reg_)<<2))
//#define CHIPTOP_WRITE(_reg_, _val_) {pr_info("PDS: CHIPTOP_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_)); }
#define CHIPTOP_WRITE(_reg_, _val_) WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_));
#define MOVDMA_READ(_reg_) READ_WORD(gMOVDMAAddr + ((_reg_)<<2))
//#define MOVDMA_WRITE(_reg_, _val_) {pr_info("PDS: MOVDMA_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_)); }
#define MOVDMA_WRITE(_reg_, _val_) WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_));
#define _HAL_MSPI_ClearDone() MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET,MSPI_CLEAR_DONE)
#define MAX_CHECK_CNT 2000
#define MSPI_READ_INDEX 0x0
#define MSPI_WRITE_INDEX 0x1
#define SPI_MIU0_BUS_BASE 0x20000000
#define SPI_MIU1_BUS_BASE 0xFFFFFFFF
// Function definitions.
//
int z80io_init(void);
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData);
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData);
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData);
#ifdef NOTNEEDED
uint8_t z80io_PRL_Send8(uint8_t txData);
uint8_t z680io_PRL_Send16(uint16_t txData);
#endif
uint8_t z80io_PRL_Read8(uint8_t dataFlag);
uint16_t z80io_PRL_Read16(void);
uint8_t z80io_SPI_Test(void);
uint8_t z80io_PRL_Test(void);
uint8_t z80io_Z80_TestMemory(void);
extern void MHal_GPIO_Init(void);
extern void MHal_GPIO_Pad_Set(uint8_t u8IndexGPIO);
extern int MHal_GPIO_PadGroupMode_Set(uint32_t u32PadMode);
extern int MHal_GPIO_PadVal_Set(uint8_t u8IndexGPIO, uint32_t u32PadMode);
extern void MHal_GPIO_Pad_Oen(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pad_Odn(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_Level(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_InOut(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_Low(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_Low(uint8_t u8IndexGPIO);
extern void MHal_Enable_GPIO_INT(uint8_t u8IndexGPIO);
extern int MHal_GPIO_To_Irq(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_POLARITY(uint8_t u8IndexGPIO, uint8_t reverse);
extern void MHal_GPIO_Set_Driving(uint8_t u8IndexGPIO, uint8_t setHigh);
extern void MHal_GPIO_PAD_32K_OUT(uint8_t u8Enable);
#ifdef __cplusplus
}
#endif
#endif // Z80IO_H

View File

@@ -1,541 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io_test.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface Test Methods
// This file contains the methods used to test the SOM to CPLD interface and evaluate
// it's performance. Production builds wont include these methods.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/sched.h>
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
uint8_t z80io_Z80_TestMemory(void)
{
// Locals.
//
uint32_t addr;
uint32_t fullCmd;
uint8_t cmd;
struct timeval start, stop;
uint32_t iterations = 100;
uint32_t errorCount;
uint32_t idx;
long totalTime;
long bytesMSec;
uint8_t result;
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E80000 | (0x82 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E20000 | (0x58 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E00000 | (0xF7 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E90000 | (0x0F << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xCF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xFF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
pr_info("Z80 Host Test - IO.\n");
for(idx=0; idx < 1000000; idx++)
{
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
SPI_SEND32(0xD0000000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0100000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0200000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0300000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0400000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0500000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
}
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000| ((uint8_t)addr) << 8 | CPLD_CMD_WRITEIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Read performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible IO ports and write to it.
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000 | ((uint8_t)addr) << 8 | CPLD_CMD_READIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing RAM Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write performance (opt).\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Fetch performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Read performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Fetch performance.\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x10;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Read performance (opt).\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x20;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
// Go through all the accessible attribute VRAM and initialise it.
pr_info("Z80 Host Test - Testing VRAM Write performance.\n");
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
iterations = 256*10;
do_gettimeofday(&start);
for(addr=0xD800; addr < 0xE000; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD800)
{
fullCmd = (addr << 16) |(0x71 << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible VRAM and write to it.
for(addr=0xD000; addr < 0xD800; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD000)
{
fullCmd = (addr << 16) | ((uint8_t)idx << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)((1*iterations*0x800)+0x800)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
return(0);
}
// A simple test to verify the SOM to CPLD SPI connectivity and give an estimate of its performance.
// The performance is based on the SPI setup and transmit time along with the close and received data processing.
// In real use, the driver will just send a command and generally ignore received data so increased throughput can be achieved.
//
uint8_t z80io_SPI_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
uint16_t rxData16Last;
uint32_t rxData32;
uint32_t rxData32Last;
uint32_t errorCount;
long totalTime;
long bytesMSec;
// Place the CPLD into echo test mode.
z80io_SPI_Send8(0xfe, &rxData8);
// 1st. test, 8bit.
pr_info("SPI Test - Testing 8 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send8((uint8_t)idx, &rxData8);
if(idx > 1 && (uint8_t)(idx-1) != rxData8)
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint8_t)(idx-1), rxData8 );
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 16bit.
pr_info("SPI Test - Testing 16 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_SPI_Send16((uint16_t)idx, &rxData16);
if(idx > 0 && (uint16_t)(idx-1) != (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint16_t)(idx-1), (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)));
errorCount++;
}
rxData16Last = rxData16;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 3rd. test, 32bit.
pr_info("SPI Test - Testing 32 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send32((uint32_t)idx, &rxData32);
if(idx > 0 && (uint32_t)(idx-1) != (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint32_t)(idx-1), (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)));
errorCount++;
}
rxData32Last = rxData32;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(4*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}
// Method to test the parallel bus, verifying integrity and assessing performance.
uint8_t z80io_PRL_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
long totalTime;
long bytesMSec;
#ifdef NOTNEEDED
uint32_t errorCount;
#endif
// Place the CPLD into echo test mode.
// 1st. test, 8bit RW.
#ifdef NOTNEEDED
pr_info("Parallel Test - Testing 8 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte and readback to compare.
z80io_PRL_Send8((uint8_t)idx);
rxData8 = z80io_PRL_Read8();
if((uint8_t)idx != rxData8)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint8_t)(idx), rxData8);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 8bit Write.
pr_info("Parallel Test - Testing 8 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte.
z80io_PRL_Send8((uint8_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 3rd. test, 8bit Read.
pr_info("Parallel Test - Testing 8 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read byte.
rxData8 = z80io_PRL_Read8(0);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#ifdef NOTNEEDED
// 4th test, 16bit.
pr_info("Parallel Test - Testing 16 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_PRL_Send16((uint16_t)idx);
rxData16 = z80io_PRL_Read16();
if((uint16_t)idx != rxData16)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint16_t)(idx), rxData16);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 5th test, 16bit Write.
pr_info("Parallel Test - Testing 16 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write word.
z80io_PRL_Send16((uint16_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 6th test, 16bit Read.
pr_info("Parallel Test - Testing 16 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read word.
rxData16 = z80io_PRL_Read16();
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}

View File

@@ -1,57 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Menu
// This file contains the methods used to present a menu of options to a user to aid
// in configuration and load/save of applications and data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include "z80menu.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
void z80menu(void)
{
// Locals.
}

View File

@@ -1,44 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Interface Menu
// This file contains the declarations required to provide a menu system allowing a
// user to configure and load/save applications/data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80MENU_H
#define Z80MENU_H
#ifdef __cplusplus
extern "C" {
#endif
// Function definitions.
//
void z80menu(void);
#ifdef __cplusplus
}
#endif
#endif // Z80MENU_H

View File

@@ -1,403 +0,0 @@
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname && longopts[i].shortname < 127) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */

View File

@@ -1,57 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Menu
// This file contains the methods used to present a menu of options to a user to aid
// in configuration and load/save of applications and data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include "z80menu.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
void z80menu(void)
{
// Locals.
}

View File

@@ -1,44 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Interface Menu
// This file contains the declarations required to provide a menu system allowing a
// user to configure and load/save applications/data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80MENU_H
#define Z80MENU_H
#ifdef __cplusplus
extern "C" {
#endif
// Function definitions.
//
void z80menu(void);
#ifdef __cplusplus
}
#endif
#endif // Z80MENU_H

View File

@@ -1,40 +1,85 @@
#MODEL := MZ2000
#MODEL := MZ700
MODEL := MZ80A
#MODEL := MZ80A
#MODEL := PCW8XXX
#MODEL := PCW9XXX
KERNEL := $(PWD)/../../../linux/kernel
FUSIONX := $(PWD)/../..
CROSS := arm-linux-gnueabihf-
ccflags-y += -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__
CTRLINC += -IZeta/API -IZ80/API
ccflags-y = -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ -DTARGET_HOST_$(MODEL)=1
CTRLINC = -IZeta/API -IZ80/API -DTARGET_HOST_$(MODEL)=1
obj-m += z80drv.o
z80drv-objs += $(MODEL)/z80driver.o Z80.o $(MODEL)/z80io.o $(MODEL)/z80menu.o # emumz.o sharpmz.o osd.o
z80drv-objs += src/z80driver.o Z80.o src/z80io.o src/z80menu.o # emumz.o sharpmz.o osd.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/gpio_table.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o
all:
all:
@echo "Specify target host, ie. make <host>"
@echo "Supported hosts: MZ80A, MZ700, MZ2000, PCW8XXX, PCW9XXX"
MZ80A: MODEL_MZ80A
MZ700: MODEL_MZ700
MZ2000: MODEL_MZ2000
PCW8XXX: MODEL_PCW8XXX
PCW9XXX: MODEL_PCW9XXX
MODEL_MZ80A:
$(MAKE) MODEL=MZ80A BUILD_MZ80A
MODEL_MZ700:
$(MAKE) MODEL=MZ700 BUILD_MZ700
MODEL_MZ2000:
$(MAKE) MODEL=MZ2000 BUILD_MZ2000
MODEL_PCW8XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW8XXX
MODEL_PCW9XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW9XXX
BUILD_MZ80A: sharpbiter k64fcpu kmod z80ctrl
BUILD_MZ700: sharpbiter k64fcpu kmod z80ctrl
BUILD_MZ2000: sharpbiter k64fcpu kmod z80ctrl
BUILD_PCW8XXX: kmod z80ctrl
BUILD_PCW9XXX: kmod z80ctrl
sharpbiter:
@echo ""
@echo "Build Sharp MZ Arbiter for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/sharpbiter.c -o sharpbiter
$(CROSS)gcc $(CTRLINC) src/sharpbiter.c -o sharpbiter
k64fcpu:
@echo ""
@echo "Build K64F Daemon for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/k64fcpu.c -o k64fcpu
$(CROSS)gcc $(CTRLINC) src/k64fcpu.c -o k64fcpu
kmod:
@echo ""
@echo "Build driver for host: $(MODEL)"
make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules
z80ctrl:
@echo ""
@echo "Build z80ctrl tool for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/z80ctrl.c -o z80ctrl
$(CROSS)gcc $(CTRLINC) src/z80ctrl.c -o z80ctrl
install:
@echo "Copy kernel driver..."
@cp z80drv.ko $(FUSIONX)/modules/
@echo "Copy z80ctrl app..."
@cp z80ctrl $(FUSIONX)/bin/
@if [ -f sharpbiter ]; then\
echo "Copy sharpbiter app...";\
cp sharpbiter $(FUSIONX)/bin/;\
fi
@if [ -f k64fcpu ]; then\
echo "Copy k64fcpu app...";\
cp k64fcpu $(FUSIONX)/bin/;\
fi
clean:
make -C $(KERNEL) M=$(PWD) clean
@rm -f sharpbiter k64fcpu z80ctrl

File diff suppressed because it is too large Load Diff

View File

@@ -183,16 +183,10 @@ int getch(uint8_t wait)
return 0;
}
void delay(int number_of_seconds)
// Millisecond delay routine.
void delay(int ms_delay)
{
// Converting time into milli_seconds
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
usleep(1000 * ms_delay);
}
// Function to dump out a given section of memory via the UART.
@@ -2799,6 +2793,7 @@ void z80ResetRequest(int signalNo)
svcCacheDir(TZSVC_DEFAULT_MZF_DIR, MZF, 1);
// Start the Z80 after initialisation of the memory.
delay(500);
startZ80(TZMM_BOOT);
return;
}
@@ -2925,6 +2920,18 @@ int main(int argc, char *argv[])
printf("Failed to open the Z80 Driver, exiting...\n");
exit(1);
}
// Setup host type, at the moment this is static but as the FusionX progresses and it can be hosted in multiple hosts without firmware change, then we can pull the
// host type from the detected hardware.
#if (TARGET_HOST_MZ80A == 1)
z80Control.hostType = HW_MZ80A;
#elif (TARGET_HOST_MZ700 == 1)
z80Control.hostType = HW_MZ700;
#elif (TARGET_HOST_MZ2000 == 1)
z80Control.hostType = HW_MZ2000;
#else
#error "Unknown host, update code to accommodate."
#endif
// Register the service request handler.
signal(SIGIO, z80ServiceRequest);

View File

@@ -101,9 +101,15 @@ static t_Z80Ctrl *Z80Ctrl = NULL;
static uint8_t *Z80RAM = NULL;
static uint8_t *Z80ROM = NULL;
// Millisecond delay routine.
void delay(int ms_delay)
{
usleep(1000 * ms_delay);
}
// Method to reset the Z80 CPU.
//
void reqResetZ80(uint8_t memoryMode)
void reqResetZ80(void)
{
// Locals.
//
@@ -116,7 +122,7 @@ void reqResetZ80(uint8_t memoryMode)
// Method to start the Z80 CPU.
//
void startZ80(uint8_t memoryMode)
void startZ80(void)
{
// Locals.
//
@@ -129,7 +135,7 @@ void startZ80(uint8_t memoryMode)
// Method to stop the Z80 CPU.
//
void stopZ80(uint8_t memoryMode)
void stopZ80(void)
{
// Locals.
//
@@ -323,24 +329,42 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
stopZ80();
// Remove drivers. Remove all because an external event could have changed last configured driver.
//
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Add in the required driver.
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
#endif
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(500);
reqResetZ80();
break;
case HOTKEY_RFS80:
@@ -349,12 +373,21 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
stopZ80();
// Remove drivers. Remove all because an external event could have changed last configured driver.
//
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
@@ -368,10 +401,8 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(500);
reqResetZ80();
break;
case HOTKEY_TZFS:
@@ -379,12 +410,21 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
stopZ80();
// Remove drivers. Remove all because an external event could have changed last configured driver.
//
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
@@ -396,12 +436,6 @@ int main(int argc, char *argv[])
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// ioctlCmd.cmd = IOCTL_CMD_Z80_START;
// ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case HOTKEY_LINUX:
@@ -412,6 +446,16 @@ int main(int argc, char *argv[])
// Remove drivers. Remove all because an external event could have changed last configured driver.
//
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;

View File

@@ -46,7 +46,13 @@
#define REFRESH_BYTE_COUNT 8 // This constant controls the number of bytes read/written to the z80 bus before a refresh cycle is needed.
#define RFSH_BYTE_CNT 256 // Number of bytes we can write before needing a full refresh for the DRAM.
#define HOST_MON_TEST_VECTOR 0x4 // Address in the host monitor to test to identify host type.
#define OS_BASE_DIR "/apps/FusionX/host/MZ-80A/" // Linux base directory where all the files are stored. On a real tranZPUter this would be the SD card root dir.
#if (TARGET_HOST_MZ80A == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-80A/" // Linux base directory where all the files are stored. On a real tranZPUter this would be the SD card root dir.
#elif (TARGET_HOST_MZ700 == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-700/"
#elif (TARGET_HOST_MZ2000 == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-2000/"
#endif
#define TZFS_AUTOBOOT_FLAG OS_BASE_DIR "/TZFSBOOT.FLG" // Filename used as a flag, if this file exists in the base directory then TZFS is booted automatically.
#define TZ_MAX_Z80_MEM 0x100000 // Maximum Z80 memory available on the tranZPUter board.

View File

@@ -593,6 +593,23 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
}
else if(strcasecmp((char *)param1, "MZ80A") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
}
else if(strcasecmp((char *)param1, "MZ700") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
}
else if(strcasecmp((char *)param1, "MZ2000") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
}
else if(strcasecmp((char *)param1, "PCW") == 0)
{
printf("Add device:%s\n", (char *)param1);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_PCW;
}
if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE)
{
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
@@ -613,6 +630,22 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
}
else if(strcasecmp((char *)param1, "MZ80A") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
}
else if(strcasecmp((char *)param1, "MZ700") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
}
else if(strcasecmp((char *)param1, "MZ2000") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
}
else if(strcasecmp((char *)param1, "PCW") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_PCW;
}
if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE)
{
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
@@ -749,7 +782,7 @@ void showArgs(char *progName, struct optparse *options)
printf(" # Load contents of binary file into memory at address. default = 0x000000.\n");
printf(" = LOADMEM --file <binary filename> --addr <24 bit addr> --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM> [--offset <offset> --len <length>]\n");
printf(" = SAVE --file <filename> --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = PageTable, 4 = IOPageTable>\n");
printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = MemoryPageTable, 4 = IOPageTable> [--memorypage <0..31>]\n");
printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = MemoryPageTable, 4 = IOPageTable> [--memorypage <0..41>]\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
#if(DEBUG_ENABLED != 0)
printf(" = DEBUG --level <level> # 0 = off, 1..15 debug level, 15 is very verbose.\n");

View File

@@ -35,16 +35,41 @@
#ifndef Z80DRIVER_H
#define Z80DRIVER_H
// Build time target. Overrides if compile time definition given.
#if defined(TARGET_HOST_MZ700)
#define TARGET_HOST_MZ700 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ2000)
#define TARGET_HOST_MZ2000 1
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ80A)
#define TARGET_HOST_MZ80A 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_PCW8XXX) || defined(TARGET_HOST_PCW9XXX)
#define TARGET_HOST_PCW 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#else
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 0 // MZ80A
#define TARGET_HOST_PCW 0 // Amstrad PCW8XXX/9XXX
#endif
// Constants.
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Philip D Smart"
#define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver"
#define DRIVER_VERSION "v1.3"
#define DRIVER_VERSION_DATE "Feb 2023"
#define DRIVER_VERSION "v1.4"
#define DRIVER_VERSION_DATE "Apr 2023"
#define DRIVER_COPYRIGHT "(C) 2018-2023"
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 1 // MZ80A
#define Z80_VIRTUAL_ROM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M which is 4x512K ROMS.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M.
#define Z80_MEMORY_PAGE_SIZE 16
@@ -69,13 +94,14 @@
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_RAM_RO 0x02000000
#define MEMORY_TYPE_VIRTUAL_HW 0x01000000
#define MEMORY_TYPE_PHYSICAL_RAM_WT MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_VIRTUAL_RAM
#define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000
// Hotkeys handled.
#define HOTKEY_ORIGINAL 0xE8
#define HOTKEY_RFS80 0xE9
#define HOTKEY_RFS40 0xEA
#define HOTKEY_RFS40 0xE9
#define HOTKEY_RFS80 0xEA
#define HOTKEY_TZFS 0xEB
#define HOTKEY_LINUX 0xEC
@@ -100,22 +126,42 @@
// Approximate governor delays to regulate emulated CPU speed.
// MZ-700
#if(TARGET_HOST_MZ700 == 1)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
#define INSTRUCTION_EQUIV_FREQ_3_54MHZ 3540000
#define INSTRUCTION_EQUIV_FREQ_7MHZ 7000000
#define INSTRUCTION_EQUIV_FREQ_14MHZ 14000000
@@ -124,6 +170,7 @@
#define INSTRUCTION_EQUIV_FREQ_112MHZ 112000000
#define INSTRUCTION_EQUIV_FREQ_224MHZ 224000000
#define INSTRUCTION_EQUIV_FREQ_448MHZ 448000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 10
enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_3_54MHZ,
@@ -141,7 +188,7 @@ enum Z80_INSTRUCTION_DELAY {
RAM_DELAY_X16 = INSTRUCTION_DELAY_RAM_56MHZ,
RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_112MHZ,
RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_224MHZ,
RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_448MHZ
RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_448MHZ,
CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_3_54MHZ,
CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_7MHZ,
CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_14MHZ,
@@ -155,22 +202,43 @@ enum Z80_INSTRUCTION_DELAY {
// MZ-2000
#if(TARGET_HOST_MZ2000 == 1)
#define INSTRUCTION_DELAY_ROM_4MHZ 243
#define INSTRUCTION_DELAY_ROM_8MHZ 122
#define INSTRUCTION_DELAY_ROM_16MHZ 61
#define INSTRUCTION_DELAY_ROM_32MHZ 30
#define INSTRUCTION_DELAY_ROM_64MHZ 15
#define INSTRUCTION_DELAY_ROM_128MHZ 7
#define INSTRUCTION_DELAY_ROM_256MHZ 3
#define INSTRUCTION_DELAY_ROM_512MHZ 1
#define INSTRUCTION_DELAY_RAM_4MHZ 218
#define INSTRUCTION_DELAY_RAM_8MHZ 112
#define INSTRUCTION_DELAY_RAM_16MHZ 56
#define INSTRUCTION_DELAY_RAM_32MHZ 28
#define INSTRUCTION_DELAY_RAM_64MHZ 14
#define INSTRUCTION_DELAY_RAM_128MHZ 7
#define INSTRUCTION_DELAY_RAM_256MHZ 3
#define INSTRUCTION_DELAY_RAM_512MHZ 1
#if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 213
#define INSTRUCTION_DELAY_ROM_8MHZ 109
#define INSTRUCTION_DELAY_ROM_16MHZ 54
#define INSTRUCTION_DELAY_ROM_32MHZ 27
#define INSTRUCTION_DELAY_ROM_64MHZ 14
#define INSTRUCTION_DELAY_ROM_128MHZ 7
#define INSTRUCTION_DELAY_ROM_256MHZ 3
#define INSTRUCTION_DELAY_ROM_512MHZ 1
#define INSTRUCTION_DELAY_RAM_4MHZ 212
#define INSTRUCTION_DELAY_RAM_8MHZ 106
#define INSTRUCTION_DELAY_RAM_16MHZ 53
#define INSTRUCTION_DELAY_RAM_32MHZ 26
#define INSTRUCTION_DELAY_RAM_64MHZ 13
#define INSTRUCTION_DELAY_RAM_128MHZ 7
#define INSTRUCTION_DELAY_RAM_256MHZ 3
#define INSTRUCTION_DELAY_RAM_512MHZ 1
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240 // These values are smaller than the ROM as Rom has 1 wait state added per cycle.
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000
#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000
#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000
@@ -179,6 +247,7 @@ enum Z80_INSTRUCTION_DELAY {
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 4
enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ,
@@ -258,6 +327,7 @@ enum Z80_INSTRUCTION_DELAY {
#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 2
// Table of governor delays to be used to control run frequency,
enum Z80_INSTRUCTION_DELAY {
@@ -288,6 +358,82 @@ enum Z80_INSTRUCTION_DELAY {
};
#endif
// Amstrad PCW-8256
#if(TARGET_HOST_PCW == 1)
#if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000
#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000
#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000
#define INSTRUCTION_EQUIV_FREQ_32MHZ 32000000
#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 5
enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ,
ROM_DELAY_X2 = INSTRUCTION_DELAY_ROM_8MHZ,
ROM_DELAY_X4 = INSTRUCTION_DELAY_ROM_16MHZ,
ROM_DELAY_X8 = INSTRUCTION_DELAY_ROM_32MHZ,
ROM_DELAY_X16 = INSTRUCTION_DELAY_ROM_64MHZ,
ROM_DELAY_X32 = INSTRUCTION_DELAY_ROM_128MHZ,
ROM_DELAY_X64 = INSTRUCTION_DELAY_ROM_256MHZ,
ROM_DELAY_X128 = INSTRUCTION_DELAY_ROM_512MHZ,
RAM_DELAY_NORMAL = INSTRUCTION_DELAY_RAM_4MHZ,
RAM_DELAY_X2 = INSTRUCTION_DELAY_RAM_8MHZ,
RAM_DELAY_X4 = INSTRUCTION_DELAY_RAM_16MHZ,
RAM_DELAY_X8 = INSTRUCTION_DELAY_RAM_32MHZ,
RAM_DELAY_X16 = INSTRUCTION_DELAY_RAM_64MHZ,
RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_128MHZ,
RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_256MHZ,
RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_512MHZ,
CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_4MHZ,
CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_8MHZ,
CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_16MHZ,
CPU_FREQUENCY_X8 = INSTRUCTION_EQUIV_FREQ_32MHZ,
CPU_FREQUENCY_X16 = INSTRUCTION_EQUIV_FREQ_64MHZ,
CPU_FREQUENCY_X32 = INSTRUCTION_EQUIV_FREQ_128MHZ,
CPU_FREQUENCY_X64 = INSTRUCTION_EQUIV_FREQ_256MHZ,
CPU_FREQUENCY_X128 = INSTRUCTION_EQUIV_FREQ_512MHZ,
};
#endif
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S'
@@ -426,9 +572,12 @@ enum Z80_INSTRUCTION_DELAY {
}\
}
#define resetZ80() {\
if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\
sendSignal(Z80Ctrl->ioTask, SIGUSR1); \
setupMemory(Z80Ctrl->defaultPageMode);\
if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\
{\
sendSignal(Z80Ctrl->ioTask, SIGUSR1); \
udelay(2000);\
}\
z80_instant_reset(&Z80CPU);\
}
@@ -460,10 +609,14 @@ enum Z80_MEMORY_PROFILE {
};
enum VIRTUAL_DEVICE {
VIRTUAL_DEVICE_NONE = 0x00000000,
VIRTUAL_DEVICE_MZ80A = 0x00000001,
VIRTUAL_DEVICE_MZ700 = 0x00000002,
VIRTUAL_DEVICE_MZ2000 = 0x00000004,
VIRTUAL_DEVICE_PCW = 0x00000008,
VIRTUAL_DEVICE_RFS40 = 0x01000000,
VIRTUAL_DEVICE_RFS80 = 0x02000000,
VIRTUAL_DEVICE_RFS = 0x03000000,
VIRTUAL_DEVICE_TZPU = 0x04000000
VIRTUAL_DEVICE_TZPU = 0x04000000,
};
typedef struct {
@@ -524,14 +677,6 @@ typedef struct {
uint8_t ioReadAhead;
uint8_t ioWriteAhead;
#if(TARGET_HOST_MZ2000 == 1)
uint8_t lowMemorySwap;
#endif
#if(TARGET_HOST_MZ80A == 1)
// MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
uint8_t memSwitch;
#endif
// Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe;
uint8_t keyportShiftCtrl;
@@ -546,6 +691,7 @@ typedef struct {
// is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made.
uint32_t cpuGovernorDelayROM;
uint32_t cpuGovernorDelayRAM;
uint8_t governorSkip;
// An I/O processor, running as a User Space daemon, can register to receive signals and events.
struct task_struct *ioTask;

View File

@@ -82,6 +82,14 @@
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_READIO_WRITE_ADDR 0x38
#define CPLD_CMD_READIO_WRITE_ADDR_P1 0x39
#define CPLD_CMD_READIO_WRITE_ADDR_P2 0x3A
#define CPLD_CMD_READIO_WRITE_ADDR_P3 0x3B
#define CPLD_CMD_READIO_WRITE_ADDR_P4 0x3C
#define CPLD_CMD_READIO_WRITE_ADDR_P5 0x3D
#define CPLD_CMD_READIO_WRITE_ADDR_P6 0x3E
#define CPLD_CMD_READIO_WRITE_ADDR_P7 0x3F
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
@@ -130,7 +138,7 @@
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_READY_ADDR 0x103C18 // GPIO12
#define PAD_Z80IO_LTSTATE_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
@@ -386,8 +394,8 @@
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4)
#define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
#define SPI_SEND_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
@@ -396,9 +404,18 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND_I_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -406,10 +423,9 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
#define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -417,24 +433,63 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32i(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
pr_info("Stage 0");\
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
pr_info("Stage 1");\
#define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) != 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
pr_info("Stage 2");\
timeout = MAX_CHECK_CNT; \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \
pr_info("Stage 3");\
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SET_FRAME_SIZE() { MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_I_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND_48(_d1_, _d2_, _d3_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 6); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d3_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+2, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte

View File

@@ -54,7 +54,7 @@ uint8_t z80io_Z80_TestMemory(void)
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND_8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
@@ -185,7 +185,7 @@ uint8_t z80io_Z80_TestMemory(void)
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
@@ -223,7 +223,7 @@ uint8_t z80io_Z80_TestMemory(void)
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
@@ -254,7 +254,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
@@ -280,7 +280,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
@@ -306,7 +306,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
@@ -322,7 +322,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
SPI_SEND_8(cmd);
}
}
}

View File

@@ -0,0 +1,436 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz2000.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-2000
// This file contains the methods used to emulate the original Sharp MZ-2000 without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// System ROM's, either use the host machine ROM or preload a ROM image.
#define ROM_DIR "/apps/FusionX/host/MZ-2000/ROMS/"
#define ROM_IPL_ORIG_FILENAME ROM_DIR "mz2000_ipl.orig"
// Boot ROM rom load and size definitions.
#define ROM_BOOT_LOAD_ADDR 0x000000
#define ROM_BOOT_SIZE 0x800
// PCW control.
typedef struct {
uint8_t lowMemorySwap; // Boot mode lower memory is swapped to 0x8000:0xFFFF
uint8_t highMemoryVRAM; // Flag to indicate high memory range 0xD000:0xFFFF is assigned to VRAM.
uint8_t graphicsVRAM; // Flag to indicate graphics VRAM selected, default is character VRAM (0).
uint8_t regCtrl; // Control register.
} t_MZ2000Ctrl;
// RFS Board control.
static t_MZ2000Ctrl MZ2000Ctrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz2000SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
MZ2000Ctrl.lowMemorySwap = 0x01; // Set memory swap flag to swapped, ie. IPL mode sees DRAM 0x0000:0x7FFF swapped to 0x8000:0xFFFF and ROM pages into 0x0000.
MZ2000Ctrl.highMemoryVRAM = 0x00;
MZ2000Ctrl.graphicsVRAM = 0x00;
MZ2000Ctrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x8000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else //if(idx >= 0x8000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
// Video RAM labelled as HW as we dont want to cache it.
//else if(idx >= 0xD000 && idx < 0xE000)
// {
// setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
//} else
// {
// setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
// }
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
// MZ-2000 comes up in IPL mode where lower 32K is ROM and upper 32K is RAM remapped from 0x0000.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x8000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (MZ2000Ctrl.lowMemorySwap ? idx - 0x8000 : idx));
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
pr_info("MZ-2000 Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz2000LoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz2000Init(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
// Lower memory is actually upper on startup, but ROM paged in, so set to zero.
if(idx >= 0x0000 && idx < 0x8000)
{
Z80Ctrl->ram[idx+0x8000] = 0x00;
} else
// Lower memory is paged in at 0x8000:0xFFFF
if(idx >= 0x8000 && idx < 0x10000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx-0x8000] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
if(idx >= 0x0000 && idx < 0x8000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
// Initial memory config.
mz2000SetupMemory(Z80Ctrl->defaultPageMode);
// mz2000LoadROM(ROM_IPL_ORIG_FILENAME, ROM_BOOT_LOAD_ADDR, ROM_BOOT_SIZE);
pr_info("Enabling MZ-2000 driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz2000Remove(void)
{
pr_info("Removing MZ-2000 driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz2000DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
switch(address)
{
default:
break;
}
} else
{
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// 8255 - Port A
case IO_ADDR_E0:
break;
// 8255 - Port B
case IO_ADDR_E1:
break;
// 8255 - Port C
// Bit 3 - L = Reset and enter IPL mode.
// Bit 1 - H = Set memory to normal state and reset cpu, RAM 0x0000:0xFFFF, L = no change.
case IO_ADDR_E2:
if(data & 0x01)
data = 0x03;
else if((data & 0x08) == 0)
data = 0x06;
else
break;
// 8255 - Control Port
// Bit 7 - H = Control word, L 3:1 define port C bit, bit 0 defines its state.
case IO_ADDR_E3:
//pr_info("E3:%02x\n", data);
// Program control register.
if(data & 0x80)
{
// Do nothing, this is the register which sets the 8255 mode.
} else
{
switch((data >> 1) & 0x07)
{
// NST toggle.
case 1:
// NST pages in all RAM and resets cpu.
if(data & 0x01)
{
MZ2000Ctrl.lowMemorySwap = 0;
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(Z80Ctrl->defaultPageMode == USE_PHYSICAL_RAM)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
//resetZ80();
}
break;
// IPL start.
case 3:
// If IPL is active (L), reconfigure memory for power on state.
if((data & 0x01) == 0)
{
mz2000SetupMemory(Z80Ctrl->defaultPageMode);
}
break;
default:
break;
}
}
break;
// Port A - Z80 PIO, contains control bits affecting memory mapping.
// Bit
// 7 - Assign address range 0xD000:0xFFFF to V-RAM when H, when L assign RAM
// 6 - Character VRAM (H), Graphics VRAM (L)
// 4 - Change screen to 80 Char (H), 40 Char (L)
// NB. When the VRAM is paged in, if Character VRAM is selected, range 0xD000:0xD7FF is VRAM, 0xC000:0xCFFF, 0xE000:0xFFFF is RAM.
case IO_ADDR_E8:
// High memory being assigned to VRAM or reverting?
if(MZ2000Ctrl.highMemoryVRAM && (data & 0x80) == 0)
{
for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(Z80Ctrl->defaultPageMode == USE_PHYSICAL_RAM)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (MZ2000Ctrl.lowMemorySwap ? idx - 0x8000 : idx));
}
}
MZ2000Ctrl.highMemoryVRAM = 0;
} else
// If this is the first activation of the VRAM or the state of it changes, ie. character <-> graphics, then update the memory mapping.
if( (!MZ2000Ctrl.highMemoryVRAM && (data & 0x80) != 0) || (MZ2000Ctrl.highMemoryVRAM && (MZ2000Ctrl.graphicsVRAM >> 6) != (data & 0x40)) )
{
for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// Graphics RAM see's the entire range set to PHYSICAL, Character RAM only see's 0xD000:0xD7FF set to PHYSICAL.
if( ((data & 0x40) && (idx >= 0xD000 && idx < 0xD800)) || ((data & 0x40) == 0) )
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
}
MZ2000Ctrl.highMemoryVRAM = 1;
}
MZ2000Ctrl.graphicsVRAM = (data & 0x040) ? 1 : 0;
break;
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz2000Read(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz2000Write(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,481 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz700.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-700
// This file contains the methods used to emulate the original Sharp MZ-700 without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
// Apr 2023 v1.1 - Updates from the PCW/MZ2000 changes.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// PCW control.
typedef struct {
uint8_t regCtrl; // Control register.
} t_MZ700Ctrl;
// RFS Board control.
static t_MZ700Ctrl MZ700Ctrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz700SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
MZ700Ctrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0x10000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0xF000 && idx < 0x10000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
pr_info("MZ-700 Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz700LoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz700Init(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
if(idx >= 0x1000 && idx < 0xD000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
// Copy BIOS and any add-on ROMS.
if((idx >= 0x0000 && idx < 0x1000) || (idx >= 0xE800 && idx < 0x10000))
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
// Add in a test program to guage execution speed.
#if(TARGET_HOST_MZ700 == 1)
Z80Ctrl->ram[0x1200] = 0x01;
Z80Ctrl->ram[0x1201] = 0x86;
Z80Ctrl->ram[0x1202] = 0xf2;
Z80Ctrl->ram[0x1203] = 0x3e;
Z80Ctrl->ram[0x1204] = 0x15;
Z80Ctrl->ram[0x1205] = 0x3d;
Z80Ctrl->ram[0x1206] = 0x20;
Z80Ctrl->ram[0x1207] = 0xfd;
Z80Ctrl->ram[0x1208] = 0x0b;
Z80Ctrl->ram[0x1209] = 0x78;
Z80Ctrl->ram[0x120a] = 0xb1;
Z80Ctrl->ram[0x120b] = 0x20;
Z80Ctrl->ram[0x120c] = 0xf6;
Z80Ctrl->ram[0x120d] = 0xc3;
Z80Ctrl->ram[0x120e] = 0x00;
Z80Ctrl->ram[0x120f] = 0x00;
#endif
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
pr_info("Enabling MZ-700 driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz700Remove(void)
{
pr_info("Removing MZ-700 driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz700DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("MEM:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{
default:
break;
}
} else
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM | |
// OUT 0xE1 = | | |DRAM
// OUT 0xE2 = |MONITOR | |
// OUT 0xE3 = | | |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
break;
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
case IO_ADDR_E1:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// MZ-700 mode we only work in first 64K block.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
break;
// Enable MOnitor ROM in lower 4K block
case IO_ADDR_E2:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
break;
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
case IO_ADDR_E3:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Reset to power on condition memory map.
case IO_ADDR_E4:
// Lower 4K set to Monitor ROM.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
if(!Z80Ctrl->inhibitMode)
{
// Upper 12K to hardware.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
case IO_ADDR_E5:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
backupMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
Z80Ctrl->inhibitMode = 1;
break;
// Restore D000-FFFF to its original state.
case IO_ADDR_E6:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
restoreMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
}
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz700Read(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz700Write(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,382 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz80a.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-80A
// This file contains the methods used to emulate the original Sharp MZ-80A without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// 40/80 Video Module control registers.
//
#define DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
// PCW control.
typedef struct {
// MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
uint8_t memSwitch;
uint8_t regCtrl; // Control register.
} t_MZ80ACtrl;
// RFS Board control.
static t_MZ80ACtrl MZ80ACtrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz80aSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// Setup defaults.
MZ80ACtrl.memSwitch = 0x00;
MZ80ACtrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0x10000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_HW, idx);
}
else if(idx >= 0xF000 && idx < 0x10000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
if((idx >= 0x0000 && idx < 0x1000) || (idx >= 0xF000 && idx < 0x10000))
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
pr_info("MZ-80A Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz80aLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz80aInit(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
if(idx >= 0x1000 && idx < 0xD000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
MZ80ACtrl.memSwitch = 0;
// If the 40/80 Video Module board is installed, ensure 40 character mode is selected.
SPI_SEND_32(DSPCTL, CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
SPI_SEND_32(DSPCTL, CPLD_CMD_WRITE_ADDR);
pr_info("Enabling MZ-80A driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz80aRemove(void)
{
pr_info("Removing MZ-80A driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz80aDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{
// Memory map switch.
case 0xE00C: case 0xE00D: case 0xE00E: case 0xE00F:
if(readFlag)
{
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
MZ80ACtrl.memSwitch = 1;
break;
// Reset memory map switch.
case 0xE010: case 0xE011: case 0xE012: case 0xE013:
if(readFlag)
{
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (idx+0xC000));
}
}
MZ80ACtrl.memSwitch = 0;
break;
default:
break;
}
} else
{
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz80aRead(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz80aWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,379 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_pcw.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - Amstrad PCW-8xxx/PCW-9xxx
// This file contains the methods used to emulate the Amstrad PCW specific
// hardware.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// IO Ports.
#define IO_FDC_STATUS 0x00 // NEC765 FDC Status Register.
#define IO_FDC_DATA 0x01 // NEC765 FDC Data Register.
#define IO_MEMBNK0 0xF0 // Memory bank 0000:3FFF register.
#define IO_MEMBNK1 0xF1 // Memory bank 4000:7FFF register.
#define IO_MEMBNK2 0xF2 // Memory bank 8000:BFFF register.
#define IO_MEMBNK3 0xF3 // Memory bank C000:FFFF register.
#define IO_MEMLOCK 0xF4 // CPC mode memory lock range.
#define IO_ROLLERRAM 0xF5 // Set the Roller RAM address.
#define IO_VORIGIN 0xF6 // Set screen vertical origin.
#define IO_SCREENATTR 0xF7 // Set screen attributes.
#define IO_GACMD 0xF8 // Gatearray command register.
#define IO_GASTATUS 0xF8 // Gatearray status register.
// The boot code for the PCW-8256 is located within the printer controller. To avoid special hardware within the CPLD, this code is incorporated
// into this module for rapid loading into RAM.
#define ROM_DIR "/apps/FusionX/host/PCW/roms/"
#define ROM_PCW8_BOOT_FILENAME ROM_DIR "PCW8256_boot.bin"
#define ROM_PCW9_BOOT_FILENAME ROM_DIR "PCW9256_boot.bin"
// Boot ROM rom load and size definitions.
#define ROM_BOOT_LOAD_ADDR 0x000000
#define ROM_BOOT_SIZE 275
// PCW control.
typedef struct {
uint8_t regMemBank0; // Mirror of register F0, memory block select 0x0000-0x3FFF.
uint8_t regMemBank1; // Mirror of register F1, memory block select 0x4000-0x7FFF.
uint8_t regMemBank2; // Mirror of register F2, memory block select 0x8000-0xBFFF.
uint8_t regMemBank3; // Mirror of register F3, memory block select 0xC000-0xFFFF.
uint8_t regCPCPageMode; // Mirror of the CPC paging lock register F4.
uint8_t regRollerRAM; // Mirror of Roller-RAM address register.
uint8_t regCtrl; // Control register.
} t_PCWCtrl;
// RFS Board control.
static t_PCWCtrl PCWCtrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void pcwSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
PCWCtrl.regMemBank0 = 0x00;
PCWCtrl.regMemBank1 = 0x01;
PCWCtrl.regMemBank2 = 0x02;
PCWCtrl.regMemBank3 = 0x03; // Keyboard is in locations 0x3FF0 - 0x3FFF of this memory block.
PCWCtrl.regCPCPageMode = 0x00;
PCWCtrl.regRollerRAM = 0x00;
PCWCtrl.regCtrl = 0x00;
// Initialise the page pointers and memory to reflect a PCW, lower 128K is used by video logic so must always be accessed in hardware.
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0x0000 && idx < 0xFFF0)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+idx));
}
if(idx >= 0xFFF0 && idx < 0x10000)
{
// The keyboard is memory mapped into upper bytes of block 3.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (RAM_BASE_ADDR+idx));
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
// No I/O Ports on the RFS board.
pr_info("PCW Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t loadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void pcwInit(uint8_t mode)
{
// Locals.
//
uint32_t idx;
// Clear memory as previous use or malloc can leave it randomly set.
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
Z80Ctrl->ram[idx] = 0x00;
}
// Disable boot mode, we dont need to fetch the boot rom as we preload it.
SPI_SEND32( (0x00F8 << 16) | (0x00 << 8) | CPLD_CMD_WRITEIO_ADDR);
// Load boot ROM.
loadROM(mode == 0 ? ROM_PCW8_BOOT_FILENAME : ROM_PCW9_BOOT_FILENAME, ROM_BOOT_LOAD_ADDR, ROM_BOOT_SIZE);
// Reset.
//SPI_SEND32( (0x00F8 << 16) | (0x01 << 8) | CPLD_CMD_WRITEIO_ADDR);
// First two bytes to NULL as were not using the bootstrap and normal operations after bootstrap would disable the mode.
Z80Ctrl->ram[0] = 0x00;
Z80Ctrl->ram[1] = 0x00;
pr_info("Enabling PCW-%s driver.\n", mode == 0 ? "8256" : "9256");
return;
}
// Perform any de-initialisation when the driver is removed.
void pcwRemove(void)
{
pr_info("Removing PCW driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void pcwDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// IO Switch.
if(ioFlag)
{
switch(address&0xff)
{
case IO_FDC_STATUS:
//pr_info("FDC_STATUS:%02x\n", data);
break;
case IO_FDC_DATA:
//pr_info("FDC_DATA:%02x\n", data);
break;
case IO_MEMBNK0:
if(!readFlag)
{
PCWCtrl.regMemBank0 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank0;
pr_info("Setting Bank 0:%02x\n", PCWCtrl.regMemBank0);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x0000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank0 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank0*16384)+idx));
}
}
break;
case IO_MEMBNK1:
if(!readFlag)
{
PCWCtrl.regMemBank1 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank1;
pr_info("Setting Bank 1:%02x\n", PCWCtrl.regMemBank1);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x4000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank1 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank1*16384)+idx));
}
}
break;
case IO_MEMBNK2:
if(!readFlag)
{
PCWCtrl.regMemBank2 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank2;
pr_info("Setting Bank 2:%02x\n", PCWCtrl.regMemBank2);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x8000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank2 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank2*16384)+idx));
}
}
break;
case IO_MEMBNK3:
if(!readFlag)
{
PCWCtrl.regMemBank3 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank3;
pr_info("Setting Bank 3:%02x\n", PCWCtrl.regMemBank3);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx < 0x3FF0)
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank3 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank3*16384)+idx));
}
}
break;
case IO_MEMLOCK:
if(!readFlag)
{
pr_info("MEMLOCK:%02x\n", data);
PCWCtrl.regCPCPageMode = data;
}
break;
case IO_ROLLERRAM:
if(!readFlag)
{
pr_info("********RollerRAM********:%02x => %04x\n", data, (((data >> 5)&0x7) * 16384)+((data&0x1f)*512));
PCWCtrl.regRollerRAM = data;
}
break;
case IO_VORIGIN:
pr_info("VORIGIN:%02x\n", data);
break;
case IO_SCREENATTR:
pr_info("SCREENATTR:%02x\n", data);
break;
case IO_GACMD:
pr_info("GACMD:%02x\n", data);
break;
default:
pr_info("Unknown:ADDR:%02x,%02x\n", address&0xff, data);
break;
}
} else
// Memory map switch.
{
switch(address)
{
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t pcwRead(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
// Return the contents of the ROM at given address.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
break;
}
}
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3) pr_info("PCW-Read:%04x, BK0:%02x, BK1:%02x, BK2:%02x, BK3:%02x, CTRL:%02x\n", address, PCWCtrl.regMemBank0, PCWCtrl.regMemBank1, PCWCtrl.regMemBank2, PCWCtrl.regMemBank3, PCWCtrl.regCtrl);
#endif
return(data);
}
// Method to handle writes.
static inline void pcwWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
// Any unprocessed write is commited to RAM.
writeVirtualRAM(address, data);
break;
}
}
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3) pr_info("PCW-Write:%04x, BK0:%02x, BK1:%02x, BK2:%02x, BK3:%02x, CTRL:%02x\n", address, PCWCtrl.regMemBank0, PCWCtrl.regMemBank1, PCWCtrl.regMemBank2, PCWCtrl.regMemBank3, PCWCtrl.regCtrl);
#endif
return;
}

View File

@@ -67,6 +67,10 @@
#define BNKSELUSER 0xEFFE // Select RFS Bank2 (User ROM)
#define BNKCTRL 0xEFFF // Bank Control register (read/write).
// 40/80 Video Module control registers.
//
#define DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
//
// RFS v2 Control Register constants.
//
@@ -87,7 +91,13 @@
#define BNKCTRLDEF BBMOSI+SDCS+BBCLK // Default on startup for the Bank Control register.
// RFS Board ROM rom filename definitions.
#define ROM_DIR "/apps/FusionX/host/MZ-80A/RFS/"
#if(TARGET_HOST_MZ80A == 1)
#define ROM_DIR "/apps/FusionX/host/MZ-80A/RFS/"
#elif(TARGET_HOST_MZ700 == 1)
#define ROM_DIR "/apps/FusionX/host/MZ-700/RFS/"
#else
#error "Unknown host configured."
#endif
#define ROM_MROM_40C_FILENAME ROM_DIR "MROM_256_40c.bin"
#define ROM_USER_I_40C_FILENAME ROM_DIR "USER_ROM_256_40c.bin"
#define ROM_USER_II_40C_FILENAME ROM_DIR "USER_ROM_II_256_40c.bin"
@@ -167,6 +177,7 @@ typedef struct {
uint8_t upCntr; // Register enable up counter.
uint32_t mromAddr; // Actual address in MROM of active bank.
uint32_t uromAddr; // Actual address in UROM of active bank.
uint8_t memSwitch; // MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
t_SDCtrl sd; // SD Control.
} t_RFSCtrl;
@@ -194,7 +205,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
RFSCtrl.regCtrl = 0x00;
RFSCtrl.mromAddr = MROM_ADDR;
RFSCtrl.uromAddr = USER_ROM_I_ADDR;
Z80Ctrl->memSwitch = 0;
RFSCtrl.memSwitch = 0;
RFSCtrl.sd.trainingCnt = 0;
RFSCtrl.sd.initialised = 0;
RFSCtrl.sd.dataOutFlag = 0;
@@ -214,7 +225,19 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));
}
if(idx >= 0xE800 && idx < 0xF000)
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{
// Memory is both ROM and hardware, the registers share the same address space.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_HW, (RFSCtrl.uromAddr+(idx-0xE800)));
@@ -224,6 +247,18 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, (idx+(Z80_VIRTUAL_ROM_SIZE - 0x10000)));
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// No I/O Ports on the RFS board.
pr_info("RFS Memory Setup complete.\n");
@@ -231,7 +266,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
// Method to load a ROM image into the ROM memory.
//
uint8_t loadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
uint8_t rfsLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
@@ -264,10 +299,10 @@ void rfsInit(uint8_t mode80c)
uint32_t idx;
// Load ROMS according to the display configuration, 40 char = standard, 80 char = 40/80 board installed.
loadROM(mode80c == 0 ? ROM_MROM_40C_FILENAME : ROM_MROM_80C_FILENAME, ROM_MROM_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_I_40C_FILENAME : ROM_USER_I_80C_FILENAME, ROM_USER_I_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_II_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_II_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_III_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_III_LOAD_ADDR, ROM_MROM_SIZE);
rfsLoadROM(mode80c == 0 ? ROM_MROM_40C_FILENAME : ROM_MROM_80C_FILENAME, ROM_MROM_LOAD_ADDR, ROM_MROM_SIZE);
rfsLoadROM(mode80c == 0 ? ROM_USER_I_40C_FILENAME : ROM_USER_I_80C_FILENAME, ROM_USER_I_LOAD_ADDR, ROM_MROM_SIZE);
rfsLoadROM(mode80c == 0 ? ROM_USER_II_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_II_LOAD_ADDR, ROM_MROM_SIZE);
rfsLoadROM(mode80c == 0 ? ROM_USER_III_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_III_LOAD_ADDR, ROM_MROM_SIZE);
// Copy the Floppy ROM to the top portion of ROM. USER III isnt normally used and if it is, 4K will be free.
for(idx=0xF000; idx < 0x10000; idx++)
@@ -276,7 +311,13 @@ void rfsInit(uint8_t mode80c)
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx+(Z80_VIRTUAL_ROM_SIZE-0x10000)] = z80io_PRL_Read8(1);
}
pr_info("Enabling RFS driver.\n");
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
pr_info("Enabling RFS(%d) driver.\n", mode80c == 1 ? 80 : 40);
return;
}
@@ -304,7 +345,7 @@ static inline void rfsDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));
}
Z80Ctrl->memSwitch = 0x01;
RFSCtrl.memSwitch = 0x01;
}
// Reset memory map switch.
@@ -318,7 +359,7 @@ static inline void rfsDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
}
}
Z80Ctrl->memSwitch = 0x00;
RFSCtrl.memSwitch = 0x00;
}
}
}
@@ -755,7 +796,7 @@ static inline void rfsWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
// Update memory map to reflect register change.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(Z80Ctrl->memSwitch)
if(RFSCtrl.memSwitch)
{
// Monitor ROM is located at 0xC000.
setMemoryType((0xC000+idx)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));

View File

@@ -17,6 +17,7 @@
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Feb 2023 v1.0 - Initial write based on the tranZPUter SW hardware.
// Apr 2023 v1.1 - Updates & bug fixes.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -72,6 +73,9 @@ typedef struct {
// TZPU Board control.
static t_TZPUCtrl TZPUCtrl;
// Forward prototypes.
static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag);
//-------------------------------------------------------------------------------------------------------------------------------
//
//
@@ -115,15 +119,145 @@ static t_TZPUCtrl TZPUCtrl;
// 30 - All memory and IO are on the tranZPUter board, 64K block 6 selected.
// 31 - All memory and IO are on the tranZPUter board, 64K block 7 selected.
// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. This sets up the default
// as the memory map changes according to selection and handled in-situ.
void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The tranZPUter SW uses a CPLD to set a 4K Z80 memory range window into a 512K-1MB linear RAM block. The actual map required
// at any one time is governed by the Memory Config register at I/O port 0x60.
// This method sets the initial state, which is a normal Sharp operating mode, all memory and IO (except tranZPUter
// control IO block) are on the mainboard.
// Setup defaults.
TZPUCtrl.clkSrc = 0x00; // Clock defaults to host.
TZPUCtrl.regCmd = 0x00; // Default for the CPLD Command.
TZPUCtrl.regCmdStatus = 0x00; // Default for the CPLD Command Status.
TZPUCtrl.regCpuCfg = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
TZPUCtrl.regCpuInfo = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
// Setup the CPLD status value, this is used by the host for configuration of tzfs.
#if(TARGET_HOST_MZ80A == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ80A;
#endif
#if(TARGET_HOST_MZ700 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ700;
#endif
#if(TARGET_HOST_MZ2000 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ2000;
#endif
TZPUCtrl.regCpldCfg = 0x00; // Not used, as no CPLD available, but need to store/return value if addressed.
// Default memory mode, TZFS.
Z80Ctrl->memoryMode = TZMM_ORIG;
// Reset IO mapping.
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// I/O Ports on the tranZPUter SW board. All hosts have the same ports for the tzpu board.
for(idx=0x0000; idx < IO_PAGE_SIZE; idx+=0x0100)
{
Z80Ctrl->iopage[idx+IO_TZ_CTRLLATCH] = IO_TZ_CTRLLATCH | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SETXMHZ] = IO_TZ_SETXMHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SET2MHZ] = IO_TZ_SET2MHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CLKSELRD] = IO_TZ_CLKSELRD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SVCREQ] = IO_TZ_SVCREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SYSREQ] = IO_TZ_SYSREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCMD] = IO_TZ_CPLDCMD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDSTATUS] = IO_TZ_CPLDSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUCFG] = IO_TZ_CPUCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUSTATUS] = IO_TZ_CPUSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUINFO] = IO_TZ_CPUINFO | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCFG] = IO_TZ_CPLDCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDINFO] = IO_TZ_CPLDINFO | IO_TYPE_VIRTUAL_HW;
}
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
pr_info("TZPU Memory Setup complete.\n");
}
// Method to load a ROM image into the ROM memory.
//
uint8_t tzpuLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->rom[loadAddr], loadSize);
if(noBytes < loadSize)
{
// pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void tzpuInit(void)
{
// Setup all initial TZFS memory modes
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_ORIG, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS2, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS3, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS4, 1);
// Ensure memory configuration is correct before requesting K64F to load Bios.
tzpuSetupMemory(USE_VIRTUAL_RAM);
// Default memory mode, TZFS.
Z80Ctrl->memoryMode = TZMM_TZFS;
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// New memory maps setup, perform a reset so that the K64F CPU loads the required ROMS.
sendSignal(Z80Ctrl->ioTask, SIGUSR1);
pr_info("Enabling TZPU driver.\n");
}
// Perform any de-initialisation when the driver is removed.
void tzpuRemove(void)
{
// Locals.
uint32_t idx;
// Go through and clear all memory maps, leave the original page in slot 0.
for(idx=1; idx < MEMORY_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{
kfree(Z80Ctrl->page[idx]);
Z80Ctrl->page[idx] = NULL;
}
}
// Default memory mode, ORIG.
Z80Ctrl->memoryMode = TZMM_ORIG;
pr_info("Removing TZPU driver.\n");
return;
}
@@ -133,27 +267,154 @@ void tzpuRemove(void)
static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// I/O or Memory?
if(ioFlag == 0)
{
// Memory map switch.
if(readFlag == 0)
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("MEM:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{
} else
{
default:
break;
}
} else
// I/O Decoding.
{
// Only lower 8 bits recognised in the tzpu.
switch(address & 0xFF)
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
default:
// MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM | |
// OUT 0xE1 = | | |DRAM
// OUT 0xE2 = |MONITOR | |
// OUT 0xE3 = | | |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
break;
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
case IO_ADDR_E1:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// MZ-700 mode we only work in first 64K block.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
break;
// Enable MOnitor ROM in lower 4K block
case IO_ADDR_E2:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
break;
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
case IO_ADDR_E3:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Reset to power on condition memory map.
case IO_ADDR_E4:
// Lower 4K set to Monitor ROM.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
if(!Z80Ctrl->inhibitMode)
{
// Upper 12K to hardware.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
case IO_ADDR_E5:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
backupMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
Z80Ctrl->inhibitMode = 1;
break;
// Restore D000-FFFF to its original state.
case IO_ADDR_E6:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
restoreMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
}
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
}
}
@@ -252,7 +513,9 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
switch(address & 0x00FF)
{
case IO_TZ_CTRLLATCH:
//pr_info("CTRLLATCH:%02x\n", data);
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("CTRLLATCH:%02x\n", data);
#endif
// Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define.
Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1));
@@ -264,13 +527,13 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
(Z80Ctrl->page[Z80Ctrl->memoryMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL);
if ((Z80Ctrl->page[Z80Ctrl->memoryMode]) == NULL)
{
pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode);
pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode);
Z80Ctrl->page[Z80Ctrl->memoryMode] = Z80Ctrl->page[0];
}
// A lot of the memory maps below are identical, minor changes such as RAM bank. This is a direct conversion of the VHDL code from the CPLD.
//
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
switch(Z80Ctrl->memoryMode)
{
@@ -744,71 +1007,3 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
return;
}
// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. This sets up the default
// as the memory map changes according to selection and handled in-situ.
void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The tranZPUter SW uses a CPLD to set a 4K Z80 memory range window into a 512K-1MB linear RAM block. The actual map required
// at any one time is governed by the Memory Config register at I/O port 0x60.
// This method sets the initial state, which is a normal Sharp operating mode, all memory and IO (except tranZPUter
// control IO block) are on the mainboard.
// Setup defaults.
TZPUCtrl.clkSrc = 0x00; // Clock defaults to host.
TZPUCtrl.regCmd = 0x00; // Default for the CPLD Command.
TZPUCtrl.regCmdStatus = 0x00; // Default for the CPLD Command Status.
TZPUCtrl.regCpuCfg = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
TZPUCtrl.regCpuInfo = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
// Setup the CPLD status value, this is used by the host for configuration of tzfs.
#if(TARGET_HOST_MZ80A == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ80A;
#endif
#if(TARGET_HOST_MZ700 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ700;
#endif
#if(TARGET_HOST_MZ2000 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ2000;
#endif
TZPUCtrl.regCpldCfg = 0x00; // Not used, as no CPLD available, but need to store/return value if addressed.
// Go through and clear all memory maps, valid for startup and reset.
for(idx=0; idx < MEMORY_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{
kfree(Z80Ctrl->page[idx]);
Z80Ctrl->page[idx] = NULL;
}
}
// Setup all initial TZFS memory modes
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_ORIG, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS2, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS3, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS4, 1);
Z80Ctrl->memoryMode = 0x02; // Default memory mode, MZ-80A.
// I/O Ports on the tranZPUter SW board. All hosts have the same ports for the tzpu board.
for(idx=0x0000; idx < 0x10000; idx+=0x0100)
{
Z80Ctrl->iopage[idx+IO_TZ_CTRLLATCH] = IO_TZ_CTRLLATCH | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SETXMHZ] = IO_TZ_SETXMHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SET2MHZ] = IO_TZ_SET2MHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CLKSELRD] = IO_TZ_CLKSELRD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SVCREQ] = IO_TZ_SVCREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SYSREQ] = IO_TZ_SYSREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCMD] = IO_TZ_CPLDCMD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDSTATUS] = IO_TZ_CPLDSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUCFG] = IO_TZ_CPUCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUSTATUS] = IO_TZ_CPUSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUINFO] = IO_TZ_CPUINFO | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCFG] = IO_TZ_CPLDCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDINFO] = IO_TZ_CPLDINFO | IO_TYPE_VIRTUAL_HW;
}
pr_info("TZPU Memory Setup complete.\n");
}