MZ700 and MZ1500 updates

This commit is contained in:
Philip Smart
2023-07-14 22:12:50 +01:00
parent 5fa8d1fada
commit dcb308f0df
17 changed files with 936 additions and 426 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -14,6 +14,7 @@
// History: v1.0 Feb 2023 - Initial write of the Sharp MZ series hardware interface software.
// v1.01 Mar 2023 - Bug fixes and additional ESC sequence processing.
// v1.02 May 2023 - Updates to accommodate MZ-1500 host.
// v1.2 Jul 2023 - Updates to MZ-1500 to display lower case chars and bug fixes.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -2505,26 +2506,26 @@ uint8_t mzInitMBHardware(void)
READ_HARDWARE_INIT(1, MBADDR_SCLDSP); // Hardware scroll needs to be reset.
// Disable the hardware sound output.
WRITE_HARDWARE(0, MBADDR_SUNDG, 0x00); // Sound could be enabled on start, disable it.
WRITE_HARDWARE(1, MBADDR_SUNDG, 0x00); // Sound could be enabled on start, disable it.
#if (TARGET_HOST_MZ1500 == 1)
for(idx=1; idx < 4; idx++)
{
WRITE_HARDWARE(0, IO_ADDR_E5, 0x00); // Select the PCG Bank.
WRITE_HARDWARE_IO(1, IO_ADDR_E5, 0x00); // Select the PCG Bank.
for(idx2=0xD000; idx2 < 0xF000; idx2++)
{
WRITE_HARDWARE(0, idx2, 0x00); // Zero the PCG memory in the bank.
WRITE_HARDWARE(1, idx2, 0x00); // Zero the PCG memory in the bank.
}
}
WRITE_HARDWARE(0, IO_ADDR_E6, 0x00); // Deselect the PCG Bank.
WRITE_HARDWARE(0, IO_PCG_PRIO, 0x00); // Set text as priority.
WRITE_HARDWARE_IO(1, IO_ADDR_E6, 0x00); // Deselect the PCG Bank.
WRITE_HARDWARE_IO(1, IO_PCG_PRIO, 0x00); // Set text as priority.
for(idx=0x11; idx < 0x90; idx+=0x11)
{
WRITE_HARDWARE(0, IO_PALETTE, idx); // Setup palettes.
WRITE_HARDWARE_IO(1, IO_PALETTE, idx); // Setup palettes.
}
for(idx=0x9F; idx <- 0xFF; idx+=0x20)
for(idx=0x9F; idx <= 0xFF; idx+=0x20)
{
WRITE_HARDWARE(0, IO_PSG_BOTH, idx); // Setup PSG.
WRITE_HARDWARE_IO(1, IO_PSG_BOTH, idx); // Setup PSG.
}
#endif

View File

@@ -14,6 +14,7 @@
// History: v1.0 Feb 2023 - Initial write of the Sharp MZ series hardware interface software.
// v1.01 Mar 2023 - Bug fixes and additional ESC sequence processing.
// v1.02 May 2023 - Updates to accommodate MZ-1500 host.
// v1.2 Jul 2023 - Fixed MZ-1500 ATB Display of lower case characters.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -369,6 +370,33 @@
}
#define READ_KEYB_INIT() READ_HARDWARE_IO_INIT(0, MBADDR_PIOB)
#define READ_KEYB() READ_HARDWARE_IO()
#elif (TARGET_HOST_MZ1500 ==1)
#define ENABLE_VIDEO() {}
#define DISABLE_VIDEO() {}
#define WRITE_VRAM_CHAR(__addr__,__data__,__xlat__) if(__xlat__ == 1)\
{\
if(islower(__data__))\
{\
WRITE_HARDWARE(0,__addr__,(dispCodeMap[(int)(__data__)].dispCode & 0x7F));\
READ_HARDWARE_INIT(0,(__addr__+0x800));\
WRITE_HARDWARE(0,(__addr__+0x800),(READ_HARDWARE() | 0x80));\
} else\
{\
WRITE_HARDWARE(0,__addr__,dispCodeMap[(int)(__data__)].dispCode);\
READ_HARDWARE_INIT(0,(__addr__+0x800));\
WRITE_HARDWARE(0,(__addr__+0x800),(READ_HARDWARE() & 0x7F));\
}\
} else\
{\
WRITE_HARDWARE(0,__addr__, __data__);\
}
#define WRITE_VRAM_ATTRIBUTE(__addr__,__data__) {\
READ_HARDWARE_INIT(0,(__addr__));\
WRITE_HARDWARE(0,__addr__,((READ_HARDWARE() & 0x80) | (__data__ & 0x7F)));\
}
#define WRITE_KEYB_STROBE(__data__) WRITE_HARDWARE(0, MBADDR_KEYPA, __data__)
#define READ_KEYB_INIT() READ_HARDWARE_INIT(0, MBADDR_KEYPB)
#define READ_KEYB() READ_HARDWARE()
#else
#define ENABLE_VIDEO() {}
#define DISABLE_VIDEO() {}

View File

@@ -19,6 +19,7 @@
// being the TTY will continue to run within the mirrored framebuffer
// and when reselected, refresh the hardware screen.
// Apr 2023 - v1.1 Updated to include MZ-2000 mode.
// Jul 2023 - v1.2 Updates and bug fixes.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -864,7 +865,7 @@ static int __init ttymz_init(void)
mzInit();
// Sign on.
sprintf(buf, "%s %s", DRIVER_DESCRIPTION, DRIVER_VERSION); mzWriteString(0, 0, buf, -1);
sprintf(buf, "%s %s", DRIVER_TITLE, DRIVER_VERSION); mzWriteString(0, 0, buf, -1);
sprintf(buf, "%s %s\n", DRIVER_COPYRIGHT, DRIVER_AUTHOR); mzWriteString(0, 1, buf, -1);
pr_info(DRIVER_DESCRIPTION " " DRIVER_VERSION "\n");

View File

@@ -12,6 +12,7 @@
//
// History: Feb 2023 - v1.0 Initial write of the Sharp MZ tty driver software.
// Apr 2023 - v1.1 Updated to include MZ-2000 mode.
// Jul 2023 - v1.2 Updates and bug fixes.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -34,15 +35,28 @@
// Constants.
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Philip D Smart"
#define DRIVER_AUTHOR "P.D.Smart"
#define DRIVER_DESCRIPTION "Sharp MZ TTY Driver"
#define DRIVER_VERSION "v1.1"
#define DRIVER_VERSION_DATE "Apr 2023"
#define DRIVER_COPYRIGHT "(C) 2018-2023"
#define DRIVER_VERSION "v1.2"
#define DRIVER_VERSION_DATE "July 2023"
#define DRIVER_COPYRIGHT "(C) 2018-23"
#define DEVICE_NAME "ttymz"
#define DRIVER_NAME "SharpMZ_tty"
#define DEBUG_ENABLED 0 // 0 = disabled, 1 .. debug level.
#define ARBITER_NAME "sharpbiter"
#if (TARGET_HOST_MZ80A == 1)
#define DRIVER_TITLE "Sharp MZ-80A TTY"
#elif (TARGET_HOST_MZ700 == 1)
#define DRIVER_TITLE "Sharp MZ-700 TTY"
#elif (TARGET_HOST_MZ1500 == 1)
#define DRIVER_TITLE "Sharp MZ-1500 TTY"
#elif (TARGET_HOST_MZ2000 == 1)
#define DRIVER_TITLE "Sharp MZ-2000 TTY"
#elif (TARGET_HOST_PCW == 1)
#define DRIVER_TITLE "Amstrad PCW-8XXX TTY"
#else
#define DRIVER_MODEL "not defined"
#endif
// Fake UART values
#define MCR_DTR 0x01

View File

@@ -128,7 +128,7 @@ enum CTRL_COMMANDS {
static t_Z80Ctrl *Z80Ctrl = NULL;
static uint8_t *Z80RAM = NULL;
static uint8_t *Z80ROM = NULL;
static uint32_t *Z80PAGE[MEMORY_MODES];
static uint32_t *Z80PAGE[MEMORY_MODES+MEMORY_SUB_MODES];
static uint8_t memoryPage = 0;
// Method to obtain and return the output screen width.
@@ -1038,7 +1038,7 @@ int main(int argc, char *argv[])
exit(1);
}
// Loop through all the memory mapping pages, each page specifies a 64K mapping block, all memory accesses go through this map.
for(idx=0; idx < MEMORY_MODES; idx++)
for(idx=0; idx < MEMORY_MODES+MEMORY_SUB_MODES; idx++)
{
// Try and bind the page, if it doesnt exist, then the pointer will be NULL so it wont be used.
Z80PAGE[idx] = (uint32_t *)mmap(0, ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + (0x1000*(idx+1))), PROT_READ | PROT_WRITE, MAP_SHARED, fdZ80, 0);

View File

@@ -40,6 +40,8 @@
// a vanilla Z80 or customisations for hosts pulled in.
// Apr 2023 - v1.4.1 Completed MZ2000 mode to work with arbiter and ttymz.
// May 2023 - v1.5 Added MZ1500 modes.
// Jul 2023 - v1.6 Updated MZ-700 code, adding sub-memory maps to increase page mapping
// speed specifically to enable reliable tape read/write.
//
//
// Notes: See Makefile to enable/disable conditional components
@@ -478,12 +480,12 @@ static zuint8 z80_read(void *context, zuint16 address)
Z_UNUSED(context)
// Only read if the address is in physical RAM.
#if(TARGET_HOST_PCW == 0)
if(isPhysical(address))
#elif(TARGET_HOST_MZ1500 == 1)
#if(TARGET_HOST_MZ1500 == 1)
// MZ-1500 take into account PCG being active, always go to hardware when active. Cannot use the map because
// this can change during PCG active mode and must be reflected when PCG is deactivated.
if(isPhysicalHW(address) || (Z80Ctrl->pcgMode == 1 && address >= 0xD000))
if(isPhysical(address) || (Z80Ctrl->pcgMode == 1 && address >= 0xD000))
#elif(TARGET_HOST_PCW == 0)
if(isPhysical(address))
#else
if(isPhysicalHW(address))
#endif
@@ -1267,10 +1269,10 @@ static int z80drv_mmap(struct file *filp, struct vm_area_struct *vma)
}
// Another one, as the memory bank page maps are allocated dynamically, need to send a size which indicates which memory block to map. This is done by the size of a memory map
// added to it the map slot as 0x1000 per slot.
else if(size >= ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + 0x1000) && size < ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + (MEMORY_MODES * 0x1000)))
else if(size >= ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + 0x1000) && size < ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + ((MEMORY_MODES+MEMORY_SUB_MODES) * 0x1000)))
{
// Loop through all the memory page slots, if active and the size is in range, then map the memory to user space.
for(idx=0; idx < MEMORY_MODES; idx++)
for(idx=0; idx < MEMORY_MODES+MEMORY_SUB_MODES; idx++)
{
if(size >= ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + ((idx+1)*0x1000)) && size < ((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)) + ((idx+2) * 0x1000)))
{
@@ -1665,6 +1667,7 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg)
case 2:
Z80Ctrl->cpuGovernorDelayROM = ROM_DELAY_X2;
Z80Ctrl->cpuGovernorDelayRAM = RAM_DELAY_X2;
pr_info("ROM:%d, RAM:%d\n", Z80Ctrl->cpuGovernorDelayROM, Z80Ctrl->cpuGovernorDelayRAM);
break;
case 4:
@@ -2092,7 +2095,7 @@ static int __init ModuleInit(void)
}
// Default memory mode is 0, ie. Original. Additional modes may be used by drivers such as the tzpu driver.
Z80Ctrl->memoryMode = 0;
for(idx=0; idx < MEMORY_MODES; idx++)
for(idx=0; idx < MEMORY_MODES+MEMORY_SUB_MODES; idx++)
{
(Z80Ctrl->page[idx]) = NULL;
}
@@ -2202,7 +2205,7 @@ static void __exit ModuleExit(void)
}
// Return the memory used for the Z80 'virtual memory' and control variables.
for(idx=0; idx < MEMORY_MODES; idx++)
for(idx=0; idx < MEMORY_MODES+MEMORY_SUB_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{

View File

@@ -17,6 +17,8 @@
// Feb 2023 - v1.2 Added RFS virtual driver.
// Apr 2023 - v1.4.1 Completed MZ2000 mode to work with arbiter and ttymz.
// May 2023 - v1.5 Added MZ1500 modes.
// Jul 2023 - v1.6 Updated MZ-700 code, adding sub-memory maps to increase page mapping
// speed specifically to enable reliable tape read/write.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -80,8 +82,8 @@
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Philip D Smart"
#define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver"
#define DRIVER_VERSION "v1.5"
#define DRIVER_VERSION_DATE "May 2023"
#define DRIVER_VERSION "v1.6"
#define DRIVER_VERSION_DATE "July 2023"
#define DRIVER_COPYRIGHT "(C) 2018-2023"
#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.
@@ -166,11 +168,11 @@
#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_3_54MHZ 240
#define INSTRUCTION_DELAY_RAM_7MHZ 120
#define INSTRUCTION_DELAY_RAM_14MHZ 60
#define INSTRUCTION_DELAY_RAM_28MHZ 30
#define INSTRUCTION_DELAY_RAM_56MHZ 15
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
@@ -599,6 +601,7 @@ enum Z80_INSTRUCTION_DELAY {
// The memory page arrays dont check for allocation due to speed, it is assumed a memory mode page has been allocated and defined prior to the memoryMode
// variable being set to that page.
#define MEMORY_MODES 32 // Maximum number of different memory modes.
#define MEMORY_SUB_MODES 6 // Number of possible alternate memory maps for a given memory mode.
#define MEMORY_PAGE_SIZE 0x10000 // Total size of directly addressable memory.
#define MEMORY_BLOCK_GRANULARITY 0x1 // Any change update MEMORY_BLOCK_SHIFT and mask in MEMORY_BLOCK_MASK
#define MEMORY_BLOCK_SHIFT 0
@@ -738,10 +741,10 @@ typedef struct {
// 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 *page[MEMORY_MODES];
uint32_t *page[MEMORY_MODES+MEMORY_SUB_MODES];
uint32_t shadowPage[MEMORY_BLOCK_SLOTS]; // Shadow page is for manipulation and backup of an existing page.
// Current memory mode as used by active driver.
// Current memory modes as used by an active driver.
uint8_t memoryMode;
// I/O Page map.

View File

@@ -13,6 +13,9 @@
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: May 2023 v1.0 - Initial write based on the MZ700 module.
// Jul 2023 - v1.6 - Updated MZ-700 code, adding sub-memory maps to increase page mapping
// speed specifically to enable reliable tape read/write. Code changes
// reflected in MZ-1500 module. Addition of MZ-1R18 emulation.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -57,6 +60,10 @@
// PCW control.
typedef struct {
uint8_t regCtrl; // Control register.
uint8_t loDRAMen; // Lower bank 0000:0FFF DRAM enabled, else monitor.
uint8_t hiDRAMen; // Higher bank D000:FFFF DRAM enabled, else memory mapped I/O.
uint32_t *ramFileMem; // 64K RamFile memory.
uint16_t ramFileAddr; // Address pointer of the MZ-1R18 64K Ram File Board memory.
} t_MZ1500Ctrl;
// RFS Board control.
@@ -71,10 +78,14 @@ static t_MZ1500Ctrl MZ1500Ctrl;
void mz1500SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint8_t subMode;
uint32_t idx;
// Setup defaults.
MZ1500Ctrl.regCtrl = 0x00;
MZ1500Ctrl.loDRAMen = 0; // Default is monitor ROM is enabled.
MZ1500Ctrl.hiDRAMen = 0; // Default is memory mapped I/O enabled.
Z80Ctrl->inhibitMode = 0;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
@@ -145,9 +156,109 @@ void mz1500SetupMemory(enum Z80_MEMORY_PROFILE mode)
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
if((idx&0x00ff) == 0xEA || (idx&0x00ff) == 0xEB)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_VIRTUAL_HW;
} else
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
}
// Setup sub-memory pages to enable rapid switch according to memory bank commands.
for(subMode=0; subMode < MEMORY_SUB_MODES; subMode++)
{
if(Z80Ctrl->page[MEMORY_MODES+subMode] == NULL)
{
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("Allocating memory sub page:%d\n", subMode);
#endif
(Z80Ctrl->page[MEMORY_MODES+subMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL);
if ((Z80Ctrl->page[MEMORY_MODES+subMode]) == NULL)
{
pr_info("z80drv: failed to allocate memory sub mapping page:%d memory!", subMode);
}
}
// Duplicate current mode into the sub page prior to setting up specific config.
memcpy((uint8_t *)Z80Ctrl->page[MEMORY_MODES+subMode], (uint8_t *)Z80Ctrl->page[0], MEMORY_BLOCK_SLOTS*sizeof(uint32_t));
Z80Ctrl->memoryMode = MEMORY_MODES + subMode;
// MZ1500 memory mode switches.
//
// MZ-1500
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = |<last> |DRAM |PCG Enable
// OUT 0xE6 = |<last> |DRAM |PCG Disable
//
// Sub-memory page maps:
//
// LOW BANK HIGH BANK PAGE MAP
// DRAM 0
// DRAM MEMORY MAP 1
// Inhibit 2
// DRAM 3
// MONITOR MEMORY MAP 4
// Inhibit 5
//
if(subMode >= 0 && subMode < 3)
{
// Enable lower 4K block as DRAM
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
if(subMode >= 3 && subMode < 6)
{
// Enable lower 4K block as Monitor ROM
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
if(subMode == 0 || subMode == 3)
{
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
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);
}
}
if(subMode == 1 || subMode == 4)
{
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0xE800; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
for(idx=0xE800; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
if(subMode == 2 || subMode == 5)
{
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
}
}
Z80Ctrl->memoryMode = MEMORY_MODES + 4;
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
@@ -195,6 +306,17 @@ void mz1500Init(uint8_t mode)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("Allocating MZ-1R18 memory\n");
#endif
// Allocate memory for the MZ-1R18 64K Ram File board.
MZ1500Ctrl.ramFileMem = (uint32_t *)kmalloc((65536 * sizeof(uint8_t)), GFP_KERNEL);
if(MZ1500Ctrl.ramFileMem == NULL)
{
pr_info("z80drv: failed to allocate MZ-1R18 Ram File memory!");
}
MZ1500Ctrl.ramFileAddr = 0x0000;
// 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.
@@ -248,17 +370,17 @@ void mz1500Remove(void)
static inline void mz1500DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
//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
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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.
//
@@ -285,92 +407,63 @@ static inline void mz1500DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint
}
} else
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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)
// Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define.
if(((address&0xFF) - 0xE0) >= 0 && ((address&0xFF) - 0xE0) < 7)
{
// MZ1500 memory mode switch.
//
// MZ-1500
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM | |
// OUT 0xE1 = | | |DRAM
// OUT 0xE2 = |MONITOR | |
// OUT 0xE3 = | | |Memory Mapped I/O
// OUT 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |PCG Enable
// OUT 0xE6 = | | |PCG Disable
// OUT 0xE5 = |<last> |DRAM |PCG Enable
// OUT 0xE6 = |<last> |DRAM |PCG Disable
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
// Sub-memory page maps:
//
// LOW BANK HIGH BANK PAGE MAP
// DRAM 0
// DRAM MEMORY MAP 1
// Inhibit 2
// DRAM 3
// MONITOR MEMORY MAP 4
// Inhibit 5
//
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
MZ1500Ctrl.loDRAMen = 1;
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-1500 mode we only work in first 64K block.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
MZ1500Ctrl.hiDRAMen = 1;
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);
}
MZ1500Ctrl.loDRAMen = 0;
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);
}
}
MZ1500Ctrl.hiDRAMen = 0;
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);
}
}
MZ1500Ctrl.loDRAMen = 0;
MZ1500Ctrl.hiDRAMen = 0;
Z80Ctrl->inhibitMode = 0;
Z80Ctrl->pcgMode = 0;
break;
// PCG Bank Switching.
@@ -390,10 +483,44 @@ static inline void mz1500DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint
Z80Ctrl->pcgMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
// Setup memory mode based on flag state.
if(Z80Ctrl->inhibitMode)
{
if(MZ1500Ctrl.loDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 2;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 5;
} else
if(MZ1500Ctrl.loDRAMen)
{
if(MZ1500Ctrl.hiDRAMen && !Z80Ctrl->pcgMode)
Z80Ctrl->memoryMode = MEMORY_MODES + 0;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 1;
} else
{
if(MZ1500Ctrl.hiDRAMen && !Z80Ctrl->pcgMode)
Z80Ctrl->memoryMode = MEMORY_MODES + 3;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 4;
}
Z80Ctrl->governorSkip = 0;
} else
if((address&0xff) >= 0xD8 && (address&0xff) < 0xDF)
{
// Do nothing for the Disk interface.
} else
if((address&0xff) >= 0xF4 && (address&0xff) < 0xF8)
{
Z80Ctrl->governorSkip = 0;
} else
{
// Z80Ctrl->governorSkip = 0;
}
}
}
@@ -406,8 +533,18 @@ static inline uint8_t mz1500Read(zuint16 address, uint8_t ioFlag)
// I/O Operation?
if(ioFlag)
{
switch(address)
switch((address&0xff))
{
// MZ-1R18 Ram File Data Register.
case 0xEA:
data = MZ1500Ctrl.ramFileMem[MZ1500Ctrl.ramFileAddr];
MZ1500Ctrl.ramFileAddr++;
break;
// MZ-1R18 Ram File Control Register.
case 0xEB:
break;
default:
break;
}
@@ -437,8 +574,19 @@ static inline void mz1500Write(zuint16 address, zuint8 data, uint8_t ioFlag)
// I/O Operation?
if(ioFlag)
{
switch(address)
switch((address&0xff))
{
// MZ-1R18 Ram File Data Register.
case 0xEA:
MZ1500Ctrl.ramFileMem[MZ1500Ctrl.ramFileAddr] = data;
MZ1500Ctrl.ramFileAddr++;
break;
// MZ-1R18 Ram File Control Register.
case 0xEB:
MZ1500Ctrl.ramFileAddr = (address & 0xff00) | data;
break;
default:
break;
}

View File

@@ -12,8 +12,11 @@
// 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.
// History: Mar 2023 - v1.0 - Initial write based on the RFS hardware module.
// Apr 2023 - v1.1 - Updates from the PCW/MZ2000 changes.
// Jul 2023 - v1.6 - Updated MZ-700 code, adding sub-memory maps to increase page mapping
// speed specifically to enable reliable tape read/write. Addition of
// MZ-1R18 emulation.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -58,6 +61,10 @@
// PCW control.
typedef struct {
uint8_t regCtrl; // Control register.
uint8_t loDRAMen; // Lower bank 0000:0FFF DRAM enabled, else monitor.
uint8_t hiDRAMen; // Higher bank D000:FFFF DRAM enabled, else memory mapped I/O.
uint32_t *ramFileMem; // 64K RamFile memory.
uint16_t ramFileAddr; // Address pointer of the MZ-1R18 64K Ram File Board memory.
} t_MZ700Ctrl;
// RFS Board control.
@@ -72,10 +79,14 @@ static t_MZ700Ctrl MZ700Ctrl;
void mz700SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint8_t subMode;
uint32_t idx;
// Setup defaults.
MZ700Ctrl.regCtrl = 0x00;
MZ700Ctrl.loDRAMen = 0; // Default is monitor ROM is enabled.
MZ700Ctrl.hiDRAMen = 0; // Default is memory mapped I/O enabled.
Z80Ctrl->inhibitMode = 0;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
@@ -146,9 +157,109 @@ void mz700SetupMemory(enum Z80_MEMORY_PROFILE mode)
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
if((idx&0x00ff) == 0xEA || (idx&0x00ff) == 0xEB)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_VIRTUAL_HW;
} else
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
}
// Setup sub-memory pages to enable rapid switch according to memory bank commands.
for(subMode=0; subMode < MEMORY_SUB_MODES; subMode++)
{
if(Z80Ctrl->page[MEMORY_MODES+subMode] == NULL)
{
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("Allocating memory sub page:%d\n", subMode);
#endif
(Z80Ctrl->page[MEMORY_MODES+subMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL);
if ((Z80Ctrl->page[MEMORY_MODES+subMode]) == NULL)
{
pr_info("z80drv: failed to allocate memory sub mapping page:%d memory!", subMode);
}
}
// Duplicate current mode into the sub page prior to setting up specific config.
memcpy((uint8_t *)Z80Ctrl->page[MEMORY_MODES+subMode], (uint8_t *)Z80Ctrl->page[0], MEMORY_BLOCK_SLOTS*sizeof(uint32_t));
Z80Ctrl->memoryMode = MEMORY_MODES + subMode;
// MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = |<last> |DRAM |Inhibit
// OUT 0xE6 = |<last> |DRAM |<return to last>
//
// Sub-memory page maps:
//
// LOW BANK HIGH BANK PAGE MAP
// DRAM 0
// DRAM MEMORY MAP 1
// Inhibit 2
// DRAM 3
// MONITOR MEMORY MAP 4
// Inhibit 5
//
if(subMode >= 0 && subMode < 3)
{
// Enable lower 4K block as DRAM
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
if(subMode >= 3 && subMode < 6)
{
// Enable lower 4K block as Monitor ROM
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
if(subMode == 0 || subMode == 3)
{
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
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);
}
}
if(subMode == 1 || subMode == 4)
{
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0xE800; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
for(idx=0xE800; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
if(subMode == 2 || subMode == 5)
{
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
}
}
Z80Ctrl->memoryMode = MEMORY_MODES + 4;
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
@@ -196,6 +307,17 @@ void mz700Init(uint8_t mode)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("Allocating MZ-1R18 memory\n");
#endif
// Allocate memory for the MZ-1R18 64K Ram File board.
MZ700Ctrl.ramFileMem = (uint32_t *)kmalloc((65536 * sizeof(uint8_t)), GFP_KERNEL);
if(MZ700Ctrl.ramFileMem == NULL)
{
pr_info("z80drv: failed to allocate MZ-1R18 Ram File memory!");
}
MZ700Ctrl.ramFileAddr = 0x0000;
// 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.
@@ -267,17 +389,17 @@ void mz700Remove(void)
static inline void mz700DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
//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
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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.
//
@@ -304,117 +426,98 @@ static inline void mz700DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8
}
} else
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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)
// Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define.
if(((address&0xFF) - 0xE0) >= 0 && ((address&0xFF) - 0xE0) < 7)
{
// 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 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
// OUT 0xE5 = |<last> |DRAM |Inhibit
// OUT 0xE6 = |<last> |DRAM |<return to last>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
// Sub-memory page maps:
//
// LOW BANK HIGH BANK PAGE MAP
// DRAM 0
// DRAM MEMORY MAP 1
// Inhibit 2
// DRAM 3
// MONITOR MEMORY MAP 4
// Inhibit 5
//
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
MZ700Ctrl.loDRAMen = 1;
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);
}
}
MZ700Ctrl.hiDRAMen = 1;
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);
}
MZ700Ctrl.loDRAMen = 0;
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);
}
}
MZ700Ctrl.hiDRAMen = 0;
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);
}
MZ700Ctrl.loDRAMen = 0;
MZ700Ctrl.hiDRAMen = 0;
Z80Ctrl->inhibitMode = 0;
break;
case IO_ADDR_E5:
Z80Ctrl->inhibitMode = 1;
break;
case IO_ADDR_E6:
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
// Setup memory mode based on flag state.
if(Z80Ctrl->inhibitMode)
{
if(MZ700Ctrl.loDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 2;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 5;
} else
if(MZ700Ctrl.loDRAMen)
{
if(MZ700Ctrl.hiDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 0;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 1;
} else
{
if(MZ700Ctrl.hiDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 3;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 4;
}
}
}
}
@@ -427,8 +530,18 @@ static inline uint8_t mz700Read(zuint16 address, uint8_t ioFlag)
// I/O Operation?
if(ioFlag)
{
switch(address)
switch((address&0xff))
{
// MZ-1R18 Ram File Data Register.
case 0xEA:
data = MZ700Ctrl.ramFileMem[MZ700Ctrl.ramFileAddr];
MZ700Ctrl.ramFileAddr++;
break;
// MZ-1R18 Ram File Control Register.
case 0xEB:
break;
default:
break;
}
@@ -458,8 +571,19 @@ static inline void mz700Write(zuint16 address, zuint8 data, uint8_t ioFlag)
// I/O Operation?
if(ioFlag)
{
switch(address)
switch((address&0xff))
{
// MZ-1R18 Ram File Data Register.
case 0xEA:
MZ700Ctrl.ramFileMem[MZ700Ctrl.ramFileAddr] = data;
MZ700Ctrl.ramFileAddr++;
break;
// MZ-1R18 Ram File Control Register.
case 0xEB:
MZ700Ctrl.ramFileAddr = (address & 0xff00) | data;
break;
default:
break;
}

View File

@@ -121,7 +121,7 @@
// SD Drive constants.
#define SD_DIR "/apps/FusionX/host/MZ-80A/RFS/"
#define SD_CARD_FILENAME SD_DIR "SHARP_MZ80A_RFS_CPM_IMAGE_1.img"// SD Card Binary Image.
#define SD_CARD_FILENAME SD_DIR "SHARP_MZ80A_RFS_CPM_IMAGE_1.img" // SD Card Binary Image.
// MMC/SD command (SPI mode)
#define CMD0 0x40 + 0 // GO_IDLE_STATE

View File

@@ -16,8 +16,11 @@
// Credits:
// 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.
// History: Feb 2023 - v1.0 - Initial write based on the tranZPUter SW hardware.
// Apr 2023 - v1.1 - Updates & bug fixes.
// Jul 2023 - v1.6 - Updated MZ-700 code, adding sub-memory maps to increase page mapping
// speed specifically to enable reliable tape read/write.
//
// Notes: See Makefile to enable/disable conditional components
//
@@ -68,6 +71,8 @@ typedef struct {
uint8_t regCpuInfo; // Internal FPGA CPU information register.
uint8_t regCpldCfg; // Internal CPLD config register.
uint8_t regCpldInfo; // Internal CPLD information register.
uint8_t loDRAMen; // Lower bank 0000:0FFF DRAM enabled, else monitor.
uint8_t hiDRAMen; // Higher bank D000:FFFF DRAM enabled, else memory mapped I/O.
} t_TZPUCtrl;
// TZPU Board control.
@@ -148,6 +153,8 @@ void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode)
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.
TZPUCtrl.loDRAMen = 0; // Default is monitor ROM is enabled.
TZPUCtrl.hiDRAMen = 0; // Default is memory mapped I/O enabled.
// Default memory mode, TZFS.
Z80Ctrl->memoryMode = TZMM_TZFS;
@@ -246,7 +253,7 @@ void tzpuRemove(void)
uint32_t idx;
// Go through and clear all memory maps, leave the original page in slot 0.
for(idx=1; idx < MEMORY_MODES; idx++)
for(idx=1; idx < MEMORY_MODES+MEMORY_SUB_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{
@@ -267,17 +274,18 @@ void tzpuRemove(void)
static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
//uint32_t idx;
// I/O or Memory?
if(ioFlag == 0)
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("MEM:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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.
//
@@ -305,117 +313,88 @@ static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_
} else
// I/O Decoding.
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3)
{
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)
// Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define.
if(((address&0xFF) - 0xE0) >= 0 && ((address&0xFF) - 0xE0) < MEMORY_SUB_MODES)
{
// 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 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
// OUT 0xE5 = |<last> |DRAM |Inhibit
// OUT 0xE6 = |<last> |DRAM |<return to last>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
TZPUCtrl.loDRAMen = 1;
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);
}
}
TZPUCtrl.hiDRAMen = 1;
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);
}
TZPUCtrl.loDRAMen = 0;
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);
}
}
TZPUCtrl.hiDRAMen = 0;
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);
}
TZPUCtrl.loDRAMen = 0;
TZPUCtrl.hiDRAMen = 0;
Z80Ctrl->inhibitMode = 0;
break;
case IO_ADDR_E5:
Z80Ctrl->inhibitMode = 1;
break;
case IO_ADDR_E6:
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
// Setup memory mode based on flag state.
if(Z80Ctrl->inhibitMode)
{
if(TZPUCtrl.loDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 2;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 5;
} else
if(TZPUCtrl.loDRAMen)
{
if(TZPUCtrl.hiDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 0;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 1;
} else
{
if(TZPUCtrl.hiDRAMen)
Z80Ctrl->memoryMode = MEMORY_MODES + 3;
else
Z80Ctrl->memoryMode = MEMORY_MODES + 4;
}
}
}
}
@@ -493,6 +472,7 @@ static inline uint8_t tzpuRead(zuint16 address, uint8_t ioFlag)
static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals
uint8_t subMode;
uint32_t idx;
// The tranZPUter board, in order to autoboot and use valuable space for variables, allows writing into the User ROM
@@ -925,6 +905,95 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
}
}
// Memory map now created/switched.
// Allocate/populate the sub-memory maps for this mode. TZFS currently uses sub-memory modes for MZ-700 page banking.
if(Z80Ctrl->memoryMode == TZMM_TZFS)
{
for(subMode=0; subMode < MEMORY_SUB_MODES; subMode++)
{
if(Z80Ctrl->page[MEMORY_MODES+subMode] == NULL)
{
#if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("Allocating memory sub page:%d\n", subMode);
#endif
pr_info("Allocating memory sub page:%d,%d\n", subMode, (MEMORY_BLOCK_SLOTS*sizeof(uint32_t)));
(Z80Ctrl->page[MEMORY_MODES+subMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL);
if ((Z80Ctrl->page[MEMORY_MODES+subMode]) == NULL)
{
pr_info("z80drv: failed to allocate memory sub mapping page:%d memory!", subMode);
}
}
// Duplicate current mode into the sub page prior to setting up specific config.
memcpy((uint8_t *)Z80Ctrl->page[MEMORY_MODES+subMode], (uint8_t *)Z80Ctrl->page[(data & (MEMORY_MODES - 1))], MEMORY_BLOCK_SLOTS*sizeof(uint32_t));
Z80Ctrl->memoryMode = MEMORY_MODES + subMode;
TZPUCtrl.loDRAMen = 0;
TZPUCtrl.hiDRAMen = 0;
Z80Ctrl->inhibitMode = 0;
// MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM |DRAM |<last>
// OUT 0xE1 = |<last> |DRAM |DRAM
// OUT 0xE2 = |MONITOR |DRAM |<last>
// OUT 0xE3 = |<last> |DRAM |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = |<last> |DRAM |Inhibit
// OUT 0xE6 = |<last> |DRAM |<return to last>
//
// Sub-memory page maps:
//
// LOW BANK HIGH BANK PAGE MAP
// DRAM 0
// DRAM MEMORY MAP 1
// Inhibit 2
// DRAM 3
// MONITOR MEMORY MAP 4
// Inhibit 5
//
if(subMode >= 0 && subMode < 3)
{
// Enable lower 4K block as DRAM
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
if(subMode == 0 || subMode == 3)
{
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
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);
}
}
if(subMode == 1 || subMode == 4)
{
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
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);
}
}
if(subMode == 2 || subMode == 5)
{
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
}
}
}
Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1));
break;
case IO_TZ_SETXMHZ:

View File

@@ -42,11 +42,15 @@ START: LD SP,STACK
CALL ?CLER ; Clear 256 bytes from NAME 10F1h to 11F0h
LD A,016H
CALL PRNT
IF KUMA = 1
LD A,0CFH
ELSE
IF MODE80C = 0
LD A,007H ; Black background, white characters. Bit 7 is clear as a write to bit 7 @ DFFFH selects 40Char mode.
ELSE
LD A,017H ; Blue background, white characters in colour mode. Bit 7 is set as a write to bit 7 @ DFFFH selects 80Char mode.
ENDIF
ENDIF
LD HL,ARAM
JR STRT1
JP 1035H ; NMI routine.
@@ -130,13 +134,19 @@ LOAD: CALL ?RDI
JR C,ST1
JP (HL)
; LOADING
MSG?2: DB 04CH, 0B7H, 0A1H, 09CH
DB 0A6H, 0B0H, 097H, 020H
DB 00DH
; SIGN ON BANNER
MSG?3: DB "** MONITOR SA-1510 **", 0DH
; SIGN ON BANNER - Different for Kuma 80 BIOS
MSG?3: IF KUMA = 0
DB "** MONITOR SA-1510 **", 0DH
ELSE
DB "*K",0A5H,0B3H,0A1H," MZ-80A M",0B7H,0B0H,0A6H
DB 096H,0B7H,09DH,"*",00DH,"*",00DH
ENDIF
; For 80 Character mode we need some space, so shorten the Check Sum Error message.
;
@@ -148,7 +158,7 @@ MSGE1: IF MODE80C = 0
DB "CK SUM?", 0DH
ENDIF
; Hook = 7 bytes.
; Hook = 7 bytes using space taken from Check Sum message.
HOOK: IF MODE80C = 1
LD A,0FFH
LD (SPAGE),A
@@ -156,7 +166,11 @@ HOOK: IF MODE80C = 1
ENDIF
; CR PAGE MODE1
.CR: CALL .MANG
.CR: IF KUMA = 1
LD HL,(DSPXY)
JP CURS2
ELSE
CALL .MANG
RRCA
JP NC,CURS2
LD L,000H
@@ -165,18 +179,43 @@ HOOK: IF MODE80C = 1
JR Z,.CP1
INC H
JP CURS1
ENDIF
.CR1: IF KUMA = 1
NEG
LD (SPAGE),A
ADD A,004H
LD (KEYPF),A
RET
DB 00EH
ENDIF
.CP1: LD (DSPXY),HL
; SCROLLER
.SCROL: LD BC,SCRNSZ - COLW ; Scroll COLW -1 lines
.SCROL: IF KUMA = 1
LD BC, 0780H
ELSE
LD BC,SCRNSZ - COLW ; Scroll COLW -1 lines
ENDIF
LD DE,SCRN ; Start of the screen.
IF KUMA = 1
LD HL,0D050H
ELSE
LD HL,SCRN + COLW ; Start of screen + 1 line.
ENDIF
LDIR
EX DE,HL
IF KUMA = 1
LD B, 050H
ELSE
LD B,COLW ; Clear last line at bottom of screen.
ENDIF
CALL ?CLER
IF KUMA = 1
JP ?RSTR
ELSE
LD BC,0001AH
ENDIF
LD DE,MANG
LD HL,MANG + 1
LDIR
@@ -587,7 +626,30 @@ TIMIN: PUSH AF
EI
RET
.DSP03: EX DE,HL
.DSP03: IF KUMA = 1
LD A,(SPAGE)
OR A
LD A,027H
RET Z
ADD A,A
INC A
RET
L03A7: PUSH BC
CALL .DSP03
LD B,A
LD A,L
CP B
POP BC
RET
L03B0: CALL .DSP03
LD L,A
XOR A
DEC H
RET
ELSE
EX DE,HL
LD (HL),001H
INC HL
LD (HL),000H
@@ -603,6 +665,7 @@ TIMIN: PUSH AF
RRCA
EX DE,HL
LD HL,(DSPXY)
ENDIF
RET
LD C,H
@@ -1141,7 +1204,11 @@ L0743: DEC H
?MODE: LD HL,KEYPF
LD (HL),08AH
LD (HL),007H
IF KUMA = 1
LD (HL),004H
ELSE
LD (HL),005H
ENDIF
LD (HL),001H
RET
@@ -1270,7 +1337,11 @@ CHGPA: XOR A
JR CHGPK1
ENDIF
CHGPK: LD A,0FFH
CHGPK1: LD (SPAGE),A
CHGPK1: IF KUMA = 1
CALL .CR1
ELSE
LD (SPAGE),A
ENDIF
LD A,0C6H
CALL ?DPCT
CHGP1: JP GETL0
@@ -1581,7 +1652,12 @@ REV2: JP ?RSTR
.MANG: LD HL,MANG
LD A,(SPAGE)
OR A
IF KUMA = 1
JR NZ,.MANG1 ; (+018H)
NOP
ELSE
JP NZ,.MANG2
ENDIF
LD A,(MGPNT)
.MANG3: SUB 008H
INC HL
@@ -2407,14 +2483,24 @@ DLY12A: CALL DLY3
CALL ?PONT
LD (HL),B
LD HL,(DSPXY)
IF KUMA = 1
CALL L03A7
ELSE
LD A,L
DSP01: CP COLW - 1 ; End of line.
ENDIF
DSP01: IF KUMA = 0
CP COLW - 1 ; End of line.
ENDIF
JR NZ,DSP04
CALL .MANG
JR C,DSP04
LD A,(SPAGE)
OR A
IF KUMA
JP NZ,CURSR
ELSE
JP NZ,.DSP03
ENDIF
EX DE,HL
LD A,B
CP 007H
@@ -2535,8 +2621,12 @@ CURSU1: CALL MGP.D
JR CURS3
CURSR: LD HL,(DSPXY)
IF KUMA = 1
CALL L03A7
ELSE
LD A,L
CP COLW - 1 ; End of line
ENDIF
JR NC,CURS2
INC L
JR CURS3
@@ -2555,8 +2645,12 @@ CURSL: LD HL,(DSPXY)
JR Z,CURS5A
DEC L
JR CURS3
CURS5A: LD L,COLW - 1 ; End of line
CURS5A: IF KUMA = 1
CALL L03B0
ELSE
LD L,COLW - 1 ; End of line
DEC H
ENDIF
JP P,CURSU1
LD H,000H
LD (DSPXY),HL
@@ -2643,6 +2737,16 @@ INST: CALL .MANG
RRCA
LD L,COLW - 1 ; End of line
LD A,L
IF KUMA = 1
JR NC,INST1B
LD A,028H
ADD A,L
LD L,A
INST1B: CALL ?PNT1
PUSH HL
LD HL,(DSPXY)
NOP
ELSE
JR NC,INST1A
INC H
INST1A: CALL ?PNT1
@@ -2650,6 +2754,7 @@ INST1A: CALL ?PNT1
LD HL,(DSPXY)
JR NC,INST2
LD A,(COLW*2)-1 ; 04FH
ENDIF
INST2: SUB L
LD B,A
POP DE
@@ -2720,6 +2825,19 @@ ROLU1: CALL MGP.I
PUSH HL
POP BC
LD DE,COLW
IF KUMA = 1
LD HL,(PAGETP)
INC B
LD A,(SPAGE)
OR A
JR Z,L0FCE ; (+008H)
LD HL,0D000H
LD E,050H
JR L0FCE ; (+001H)
L0FCD: ADD HL,DE
L0FCE: DJNZ L0FCD ; (-003H)
NOP
ELSE
LD HL,SCRN - COLW
LD A,(SPAGE)
OR A
@@ -2730,6 +2848,7 @@ ROLU1: CALL MGP.I
DEC B
JP P,?PNT2
LD B,000H
ENDIF
ADD HL,BC
RES 3,H
POP DE