Files
zSoft/common/sharpmz.c
2021-12-23 23:22:25 +00:00

2389 lines
98 KiB
C

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: sharpmz.c
// Created: December 2020
// Version: v1.0
// Author(s): Philip Smart
// Description: The Sharp MZ library.
// This file contains methods which allow the ZPU to access and control the Sharp MZ
// series computer hardware. The ZPU is instantiated within a physical Sharp MZ machine
// or an FPGA hardware emulation and provides either a host CPU running zOS or as an
// I/O processor providing services.
//
// NB. This library is NOT yet thread safe.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: v1.0 Dec 2020 - Initial write of the Sharp MZ series hardware interface 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/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__K64F__)
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <core_pins.h>
#include <usb_serial.h>
#include <Arduino.h>
#include "k64f_soc.h"
#include <../../libraries/include/stdmisc.h>
#elif defined(__ZPU__)
#include <stdint.h>
#include <stdio.h>
#include "zpu_soc.h"
#include <stdlib.h>
#include <ctype.h>
#include <stdmisc.h>
#include <string.h>
#elif defined(__M68K__)
#include <stdint.h>
#include <stdio.h>
#include "m68k_soc.h"
//#include <stdlib.h>
#include <ctype.h>
#include <stdmisc.h>
#include <string.h>
#else
#error "Target CPU not defined, use __ZPU__ or __K64F__"
#endif
#include "ff.h" /* Declarations of FatFs API */
#include "utils.h"
#include "sharpmz.h"
#ifndef __APP__ // Protected methods which should only reside in the kernel.
// --------------------------------------------------------------------------------------------------------------
// Static data declarations.
// --------------------------------------------------------------------------------------------------------------
// Global scope variables used within the zOS kernel.
//
uint32_t volatile *ms;
t_z80Control z80Control;
t_osControl osControl;
t_svcControl *svcControl = TZSVC_CMD_STRUCT_ADDR_ZOS;
// Mapping table to map Sharp MZ80A Ascii to Standard ASCII.
//
static t_asciiMap asciiMap[] = {
{ 0x00 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x00 }, { 0x20 }, { 0x20 }, // 0x0F
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x1F
{ 0x20 }, { 0x21 }, { 0x22 }, { 0x23 }, { 0x24 }, { 0x25 }, { 0x26 }, { 0x27 }, { 0x28 }, { 0x29 }, { 0x2A }, { 0x2B }, { 0x2C }, { 0x2D }, { 0x2E }, { 0x2F }, // 0x2F
{ 0x30 }, { 0x31 }, { 0x32 }, { 0x33 }, { 0x34 }, { 0x35 }, { 0x36 }, { 0x37 }, { 0x38 }, { 0x39 }, { 0x3A }, { 0x3B }, { 0x3C }, { 0x3D }, { 0x3E }, { 0x3F }, // 0x3F
{ 0x40 }, { 0x41 }, { 0x42 }, { 0x43 }, { 0x44 }, { 0x45 }, { 0x46 }, { 0x47 }, { 0x48 }, { 0x49 }, { 0x4A }, { 0x4B }, { 0x4C }, { 0x4D }, { 0x4E }, { 0x4F }, // 0x4F
{ 0x50 }, { 0x51 }, { 0x52 }, { 0x53 }, { 0x54 }, { 0x55 }, { 0x56 }, { 0x57 }, { 0x58 }, { 0x59 }, { 0x5A }, { 0x5B }, { 0x5C }, { 0x5D }, { 0x5E }, { 0x5F }, // 0x5F
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x6F
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x7F
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x8F
{ 0x20 }, { 0x20 }, { 0x65 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x74 }, { 0x67 }, { 0x68 }, { 0x20 }, { 0x62 }, { 0x78 }, { 0x64 }, { 0x72 }, { 0x70 }, { 0x63 }, // 0x9F
{ 0x71 }, { 0x61 }, { 0x7A }, { 0x77 }, { 0x73 }, { 0x75 }, { 0x69 }, { 0x20 }, { 0x4F }, { 0x6B }, { 0x66 }, { 0x76 }, { 0x20 }, { 0x75 }, { 0x42 }, { 0x6A }, // 0XAF
{ 0x6E }, { 0x20 }, { 0x55 }, { 0x6D }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x6F }, { 0x6C }, { 0x41 }, { 0x6F }, { 0x61 }, { 0x20 }, { 0x79 }, { 0x20 }, { 0x20 }, // 0xBF
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XCF
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XDF
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XEF
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 } // 0XFF
};
static t_dispCodeMap dispCodeMap[] = {
{ 0xCC }, // NUL '\0' (null character)
{ 0xE0 }, // SOH (start of heading)
{ 0xF2 }, // STX (start of text)
{ 0xF3 }, // ETX (end of text)
{ 0xCE }, // EOT (end of transmission)
{ 0xCF }, // ENQ (enquiry)
{ 0xF6 }, // ACK (acknowledge)
{ 0xF7 }, // BEL '\a' (bell)
{ 0xF8 }, // BS '\b' (backspace)
{ 0xF9 }, // HT '\t' (horizontal tab)
{ 0xFA }, // LF '\n' (new line)
{ 0xFB }, // VT '\v' (vertical tab)
{ 0xFC }, // FF '\f' (form feed)
{ 0xFD }, // CR '\r' (carriage ret)
{ 0xFE }, // SO (shift out)
{ 0xFF }, // SI (shift in)
{ 0xE1 }, // DLE (data link escape)
{ 0xC1 }, // DC1 (device control 1)
{ 0xC2 }, // DC2 (device control 2)
{ 0xC3 }, // DC3 (device control 3)
{ 0xC4 }, // DC4 (device control 4)
{ 0xC5 }, // NAK (negative ack.)
{ 0xC6 }, // SYN (synchronous idle)
{ 0xE2 }, // ETB (end of trans. blk)
{ 0xE3 }, // CAN (cancel)
{ 0xE4 }, // EM (end of medium)
{ 0xE5 }, // SUB (substitute)
{ 0xE6 }, // ESC (escape)
{ 0xEB }, // FS (file separator)
{ 0xEE }, // GS (group separator)
{ 0xEF }, // RS (record separator)
{ 0xF4 }, // US (unit separator)
{ 0x00 }, // SPACE
{ 0x61 }, // !
{ 0x62 }, // "
{ 0x63 }, // #
{ 0x64 }, // $
{ 0x65 }, // %
{ 0x66 }, // &
{ 0x67 }, // '
{ 0x68 }, // (
{ 0x69 }, // )
{ 0x6B }, // *
{ 0x6A }, // +
{ 0x2F }, // ,
{ 0x2A }, // -
{ 0x2E }, // .
{ 0x2D }, // /
{ 0x20 }, // 0
{ 0x21 }, // 1
{ 0x22 }, // 2
{ 0x23 }, // 3
{ 0x24 }, // 4
{ 0x25 }, // 5
{ 0x26 }, // 6
{ 0x27 }, // 7
{ 0x28 }, // 8
{ 0x29 }, // 9
{ 0x4F }, // :
{ 0x2C }, // ;
{ 0x51 }, // <
{ 0x2B }, // =
{ 0x57 }, // >
{ 0x49 }, // ?
{ 0x55 }, // @
{ 0x01 }, // A
{ 0x02 }, // B
{ 0x03 }, // C
{ 0x04 }, // D
{ 0x05 }, // E
{ 0x06 }, // F
{ 0x07 }, // G
{ 0x08 }, // H
{ 0x09 }, // I
{ 0x0A }, // J
{ 0x0B }, // K
{ 0x0C }, // L
{ 0x0D }, // M
{ 0x0E }, // N
{ 0x0F }, // O
{ 0x10 }, // P
{ 0x11 }, // Q
{ 0x12 }, // R
{ 0x13 }, // S
{ 0x14 }, // T
{ 0x15 }, // U
{ 0x16 }, // V
{ 0x17 }, // W
{ 0x18 }, // X
{ 0x19 }, // Y
{ 0x1A }, // Z
{ 0x52 }, // [
{ 0x59 }, // \ '\\'
{ 0x54 }, // ]
{ 0xBE }, // ^
{ 0x3C }, // _
{ 0xC7 }, // `
{ 0x81 }, // a
{ 0x82 }, // b
{ 0x83 }, // c
{ 0x84 }, // d
{ 0x85 }, // e
{ 0x86 }, // f
{ 0x87 }, // g
{ 0x88 }, // h
{ 0x89 }, // i
{ 0x8A }, // j
{ 0x8B }, // k
{ 0x8C }, // l
{ 0x8D }, // m
{ 0x8E }, // n
{ 0x8F }, // o
{ 0x90 }, // p
{ 0x91 }, // q
{ 0x92 }, // r
{ 0x93 }, // s
{ 0x94 }, // t
{ 0x95 }, // u
{ 0x96 }, // v
{ 0x97 }, // w
{ 0x98 }, // x
{ 0x99 }, // y
{ 0x9A }, // z
{ 0xBC }, // {
{ 0x80 }, // |
{ 0x40 }, // }
{ 0xA5 }, // ~
{ 0xC0 } // DEL
};
static t_scanCodeMap scanCodeMap[] = {
// NO SHIFT
{
// S0 00 - 07
ESC , // SPARE - Allocate as Escape
GRAPHKEY , // GRAPH
'_' , // Pound/Down Arrow
ALPHAKEY , // ALPHA
NOKEY , // NO
';' , // +
':' , // *
CR , // CR
// S1 08 - 0F
'y' , // y
'z' , // z
'@' , // `
'[' , // {
']' , // }
NOKEY , // NULL
NOKEY , // NULL
NOKEY , // NULL
// S2 10 - 17
'q' , // q
'r' , // r
's' , // s
't' , // t
'u' , // u
'v' , // v
'w' , // w
'x' , // x
// S3 18 - 1F
'i' , // i
'j' , // j
'k' , // k
'l' , // l
'm' , // m
'n' , // n
'o' , // o
'p' , // p
// S4 20 - 27
'a' , // a
'b' , // b
'c' , // c
'd' , // d
'e' , // e
'f' , // f
'g' , // g
'h' , // h
// S5 28 - 2F
'1' , // 1
'2' , // 2
'3' , // 3
'4' , // 4
'5' , // 5
'6' , // 6
'7' , // 7
'8' , // 8
// S6 30 - 37
'\\' , // Backslash
CURSUP , //
'-' , // -
' ' , // SPACE
'0' , // 0
'9' , // 9
',' , // ,
'.' , // .
// S7 38 - 3F
INSERT , // INST.
DELETE , // DEL.
CURSUP , // CURSOR UP
CURSDOWN , // CURSOR DOWN
CURSRIGHT, // CURSOR RIGHT
CURSLEFT , // CURSOR LEFT
'?' , // Question Mark
'/' , // Forward Slash
// S8 40 - 47 - modifier keys.
BACKS , // BREAK - Backspace without modifiers, like standard ascii keyboards.
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
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
NOKEY ,
NOKEY ,
NOKEY
},
// CAPS LOCK
{
// S0 00 - 07
ESC , // SPARE - Allocate as Escape
GRAPHKEY , // GRAPH
0x58 , //
ALPHAKEY , // ALPHA
NOKEY , // NO
':' , // ;
';' , // :
CR , // CR
// S1 08 - 0F
'Y' , // Y
'Z' , // Z
'@' , // @
'[' , // [
']' , // ]
NOKEY , // NULL
NOKEY , // NULL
NOKEY , // NULL
// S2 10 - 17
'Q' , // Q
'R' , // R
'S' , // S
'T' , // T
'U' , // U
'V' , // V
'W' , // W
'X' , // X
// S3 18 - 1F
'I' , // I
'J' , // J
'K' , // K
'L' , // L
'M' , // M
'N' , // N
'O' , // O
'P' , // P
// S4 20 - 27
'A' , // A
'B' , // B
'C' , // C
'D' , // D
'E' , // E
'F' , // F
'G' , // G
'H' , // H
// S5 28 - 2F
'1' , // 1
'2' , // 2
'3' , // 3
'4' , // 4
'5' , // 5
'6' , // 6
'7' , // 7
'8' , // 8
// S6 30 - 37
'\\' , // Backslash
CURSUP , //
'-' , // -
' ' , // SPACE
'0' , // 0
'9' , // 9
',' , // ,
'.' , // .
// S7 38 - 3F
INSERT , // INST.
DELETE , // DEL.
CURSUP , // CURSOR UP
CURSDOWN , // CURSOR DOWN
CURSRIGHT, // CURSOR RIGHT
CURSLEFT , // CURSOR LEFT
'?' , // ?
'/' , // /
// S8 40 - 47 - modifier keys.
BACKS , // BREAK - Backspace without modifiers, like standard ascii keyboards.
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
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
NOKEY ,
NOKEY ,
NOKEY
},
// SHIFT LOCK.
{
// S0 00 - 07
ESC , // SPARE - Allocate as Escape
GRAPHKEY , // GRAPH
0x58 , //
ALPHAKEY , // ALPHA
NOKEY , // NO
'+' , // ;
'*' , // :
CR , // CR
// S1 08 - 0F
'Y' , // Y
'Z' , // Z
'`' , // @
'{' , // [
'}' , // ]
NOKEY , // NULL
NOKEY , // NULL
NOKEY , // NULL
// S2 10 - 17
'Q' , // Q
'R' , // R
'S' , // S
'T' , // T
'U' , // U
'V' , // V
'W' , // W
'X' , // X
// S3 18 - 1F
'I' , // I
'J' , // J
'K' , // K
'L' , // L
'M' , // M
'N' , // N
'O' , // O
'P' , // P
// S4 20 - 27
'A' , // A
'B' , // B
'C' , // C
'D' , // D
'E' , // E
'F' , // F
'G' , // G
'H' , // H
// S5 28 - 2F
'!' , // !
'"' , // "
'#' , // #
'$' , // $
'%' , // %
'&' , // &
'\'' , // '
'(' , // (
// S6 30 - 37
'|' , // Backslash
'~' , // POND MARK
'=' , // YEN
' ' , // SPACE
' ' , // ¶
')' , // )
'<' , // <
'>' , // >
// S7 38 - 3F
CLRKEY , // CLR - END. - Clear screen.
CURHOMEKEY, // HOME. - Cursor to home.
PAGEUP , // PAGE UP - CURSOR UP
PAGEDOWN , // PAGE DOWN - CURSOR DOWN
ENDKEY , // END - CURSOR RIGHT
HOMEKEY , // HOME - CURSOR LEFT
'?' , // ? - Question Mark
'/' , // / - Forward Slash
// S8 40 - 47 - modifier keys.
BREAKKEY , // BREAK - Shift+BREAK = BREAK
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY , // SHIFT
// S9 48 - 4F - Function keys.
FUNC6 , // Function key F1
FUNC7 , // Function key F2
FUNC8 , // Function key F3
FUNC9 , // Function key F4
FUNC10 , // Function key F5
NOKEY ,
NOKEY ,
NOKEY
},
// CONTROL CODE
{
// S0 00 - 07
ESC , // SPARE - Allocate as Escape
DEBUGKEY , // GRAPH - Enable debugging output.
CTRL_CAPPA , // ^
ANSITGLKEY, // ALPHA - Toggle Ansi terminal emulator.
NOKEY , // NO
NOKEY , // ;
NOKEY , // :
NOKEY , // CR
// S1 08 - 0F
CTRL_Y , // ^Y E3
CTRL_Z , // ^Z E4 (CHECKER)
CTRL_AT , // ^@
CTRL_LB , // ^[ EB/E5
CTRL_RB , // ^] EA/E7
NOKEY , // #NULL
NOKEY , // #NULL
NOKEY , // #NULL
// S2 10 - 17
CTRL_Q , // ^Q
CTRL_R , // ^R
CTRL_S , // ^S
CTRL_T , // ^T
CTRL_U , // ^U
CTRL_V , // ^V
CTRL_W , // ^W E1
CTRL_X , // ^X E2
// S3 18 - 1F
CTRL_I , // ^I F9
CTRL_J , // ^J FA
CTRL_K , // ^K FB
CTRL_L , // ^L FC
CTRL_M , // ^M CD
CTRL_N , // ^N FE
CTRL_O , // ^O FF
CTRL_P , // ^P E0
// S4 20 - 27
CTRL_A , // ^A F1
CTRL_B , // ^B F2
CTRL_C , // ^C F3
CTRL_D , // ^D F4
CTRL_E , // ^E F5
CTRL_F , // ^F F6
CTRL_G , // ^G F7
CTRL_H , // ^H F8
// S5 28 - 2F
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
// S6 30 - 37 (ERROR? 7 VALUES ONLY!!)
NOKEY , // ^YEN E6
CTRL_CAPPA , // ^ EF
NOKEY ,
NOKEY ,
NOKEY ,
CTRL_UNDSCR , // ^,
NOKEY ,
NOKEY ,
// S7 - 38 - 3F
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
CTRL_SLASH , // ^/ EE
// S8 40 - 47 - modifier keys.
NOKEY , // BREAK - CTRL+BREAK - not yet assigned
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
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
NOKEY ,
NOKEY ,
NOKEY
},
// KANA
{
// S0 00 - 07
0xBF , // SPARE
NOKEY , // GRAPH BUT NULL
0xCF , // NIKO WH.
0xC9 , // ALPHA
NOKEY , // NO
0xB5 , // MO
0x4D , // DAKU TEN
0xCD , // CR
// 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 - modifier keys.
NOKEY , // BREAK - GRPH+BREAK - not yet assigned
NOKEY , // CTRL
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
NOKEY ,
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
NOKEY ,
NOKEY ,
NOKEY
}
};
// Mapping table of sharp special control keys to ANSI ESCape sequences.
//
static t_ansiKeyMap ansiKeySeq[] = {
{ HOMEKEY , "\x1b[1~" }, // HOME - Cursor to home.
{ CURSUP , "\x1b[A" }, // CURSOR UP
{ CURSDOWN , "\x1b[B" }, // CURSOR DOWN
{ CURSRIGHT , "\x1b[C" }, // CURSOR RIGHT
{ CURSLEFT , "\x1b[D" }, // CURSOR LEFT
{ FUNC1 , "\x1b[10~" }, // Function key 1
{ FUNC2 , "\x1b[11~" }, // Function key 2
{ FUNC3 , "\x1b[12~" }, // Function key 3
{ FUNC4 , "\x1b[13~" }, // Function key 4
{ FUNC5 , "\x1b[14~" }, // Function key 5
{ FUNC6 , "\x1b[15~" }, // Function key 6
{ FUNC7 , "\x1b[17~" }, // Function key 7
{ FUNC8 , "\x1b[18~" }, // Function key 8
{ FUNC9 , "\x1b[19~" }, // Function key 9
{ FUNC10 , "\x1b[20~" }, // Function key 10
{ INSERT , "\x1b[2~" }, // Insert.
{ DELETE , "\x1b[3~" }, // Delete.
{ ENDKEY , "\x1b[F" }, // End Key.
{ PAGEUP , "\x1b[5~" }, // Page Up.
{ PAGEDOWN , "\x1b[6~" }, // Page Down.
};
// Static structures for controlling and managing hardware features.
// Display control structure. Used to manage the display including the Ansi Terminal.
static t_displayBuffer display = { .screenAttr = 0x71, .screenRow = 0, .displayCol = 0, .displayRow = 0, .maxScreenRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS),
.maxDisplayRow = VC_MAX_ROWS, .maxScreenCol = VC_MAX_COLUMNS, .useAnsiTerm = 1, .lineWrap = 0, .debug = 0, .inDebug = 0 };
// Keyboard control structure. Used to manage keyboard sweep, mapping and store.
static t_keyboard keyboard = { .holdTimer = 0L, .autorepeat = 0, .mode = KEYB_LOWERCASE, .cursorOn = 1, .flashTimer = 0L, .keyBuf[0] = 0x00, .keyBufPtr = 0 };
// AnsiTerminal control structure. Used to manage the inbuilt Ansi Terminal.
static t_AnsiTerm ansiterm = { .state = ANSITERM_ESC, .charcnt = 0, .paramcnt = 0, .setScreenMode = 0, .setExtendedMode = 0, .saveRow = 0, .saveCol = 0 };
// Colour map for the Ansi Terminal.
const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
// --------------------------------------------------------------------------------------------------------------
// Methods
// --------------------------------------------------------------------------------------------------------------
// Method to configure the motherboard hardware after a reset.
//
uint8_t mzInitMBHardware(void)
{
// From the 1Z-013A monitor code, initialise the 8255 PIO.
//
*(volatile uint8_t *)(MBADDR_8BIT_KEYPF) = 0x8A; // 10001010 CTRL WORD MODE0
*(volatile uint8_t *)(MBADDR_8BIT_KEYPF) = 0x07; // PC3=1 M-ON
*(volatile uint8_t *)(MBADDR_8BIT_KEYPF) = 0x05; // PC2=1 INTMSK
*(volatile uint8_t *)(MBADDR_8BIT_KEYPF) = 0x01; // TZ: Enable VGATE
// Initialise the 8253 timer.
*(volatile uint8_t *)(MBADDR_8BIT_CONTF) = 0x74; // From monitor, according to system clock.
*(volatile uint8_t *)(MBADDR_8BIT_CONTF) = 0xB0; //
// Set timer in seconds, default to 0.
*(volatile uint8_t *)(MBADDR_8BIT_CONT2) = 0x00; // Timer 2 is the number of seconds.
*(volatile uint8_t *)(MBADDR_8BIT_CONT2) = 0x00; //
// Set timer in seconds, default to 0.
*(volatile uint8_t *)(MBADDR_8BIT_CONT1) = 0x0A; // Timer 1 is set to 640.6uS pulse into timer 2.
*(volatile uint8_t *)(MBADDR_8BIT_CONT1) = 0x00; //
// Set timer timer to run.
*(volatile uint8_t *)(MBADDR_8BIT_CONTF) = 0x80; //
return(0);
}
// Method to initialise the Sharp MZ extensions.
//
uint8_t mzInit(void)
{
// Initialise Sharp MZ hardware.
mzInitMBHardware();
// Clear and setup the screen mode and resolution.
mzClearScreen(3, 1);
mzSetMachineVideoMode(VMMODE_MZ700);
mzSetVGAMode(VMMODE_VGA_640x480);
mzSetVGABorder(VMBORDER_BLUE);
mzSetScreenWidth(80);
return(0);
}
// Method to clear the screen.
// mode: 0 = clear from cursor to end of screen.
// 1 = clear from 0,0 to cursor.
// 2 = clear entire screen
// 3 = clear entire screen and reset scroll buffer.
//
void mzClearScreen(uint8_t mode, uint8_t updPos)
{
// Locals.
uint32_t dstVRAMStartAddr;
uint32_t dstVRAMEndAddr;
uint32_t dstARAMStartAddr;
uint32_t startIdx;
uint32_t endIdx;
// Sanity checks.
if(mode > 3)
return;
switch(mode)
{
// Clear from cursor to end of screen.
case 0:
dstVRAMStartAddr = VIDEO_VRAM_BASE_ADDR+(display.displayRow*VC_MAX_COLUMNS)+display.displayCol;
dstVRAMEndAddr = VIDEO_VRAM_BASE_ADDR + VIDEO_VRAM_SIZE;
dstARAMStartAddr = VIDEO_ARAM_BASE_ADDR+(display.displayRow*VC_MAX_COLUMNS)+display.displayCol;
startIdx = (((display.screenRow < display.maxDisplayRow ? display.displayRow : (display.screenRow - display.maxDisplayRow + display.displayRow))) * display.maxScreenCol) + display.displayCol;
endIdx = startIdx + ((display.maxScreenCol*display.maxDisplayRow) - ((display.screenRow < display.maxDisplayRow ? display.displayRow : display.screenRow ) * display.maxScreenCol));
break;
// Clear from beginning of screen to cursor.
case 1:
dstVRAMStartAddr = VIDEO_VRAM_BASE_ADDR;
dstVRAMEndAddr = VIDEO_VRAM_BASE_ADDR+(display.displayRow*VC_MAX_COLUMNS)+display.displayCol;
dstARAMStartAddr = VIDEO_ARAM_BASE_ADDR;
startIdx = ((display.screenRow < display.maxDisplayRow ? display.screenRow : (display.screenRow - display.maxDisplayRow)) * display.maxScreenCol);
endIdx = ((display.screenRow < display.maxDisplayRow ? display.displayRow : (display.screenRow - display.maxDisplayRow + display.displayRow)) * display.maxScreenCol) + display.displayCol;
break;
// Clear entire screen.
case 2:
dstVRAMStartAddr = VIDEO_VRAM_BASE_ADDR;
dstVRAMEndAddr = VIDEO_VRAM_BASE_ADDR + VIDEO_VRAM_SIZE;
dstARAMStartAddr = VIDEO_ARAM_BASE_ADDR;
startIdx = ((display.screenRow < display.maxDisplayRow ? display.screenRow : (display.screenRow - display.maxDisplayRow)) * display.maxScreenCol);
endIdx = startIdx + (display.maxScreenCol*display.maxDisplayRow);
if(updPos)
{
display.displayRow = 0;
display.displayCol = 0;
}
break;
// Clear entire screen including scrollback buffer.
default:
dstVRAMStartAddr = VIDEO_VRAM_BASE_ADDR;
dstVRAMEndAddr = VIDEO_VRAM_BASE_ADDR+VIDEO_VRAM_SIZE;
dstARAMStartAddr = VIDEO_ARAM_BASE_ADDR;
startIdx = 0;
endIdx = VC_DISPLAY_BUFFER_SIZE;
// Reset parameters to start of screen.
if(updPos)
{
display.displayRow = 0;
display.displayCol = 0;
display.screenRow = 0;
}
break;
}
// Clear the physical character display and attribute RAM.
// Select 32bit or 8 bit clear depending on the start/end position.
//
if((dstVRAMStartAddr&0x3) == 0 && (dstVRAMEndAddr&0x3) ==0)
{
uint32_t screenAttr = display.screenAttr << 24 | display.screenAttr << 16 | display.screenAttr << 8 | display.screenAttr;
for(uint32_t dstVRAMAddr=dstVRAMStartAddr, dstARAMAddr = dstARAMStartAddr; dstVRAMAddr < dstVRAMEndAddr; dstVRAMAddr+=4, dstARAMAddr+=4)
{
*(uint32_t *)(dstVRAMAddr) = 0x00000000;
*(uint32_t *)(dstARAMAddr) = screenAttr;
}
} else
{
for(uint32_t dstVRAMAddr=dstVRAMStartAddr, dstARAMAddr = dstARAMStartAddr; dstVRAMAddr <= dstVRAMEndAddr; dstVRAMAddr+=1, dstARAMAddr+=1)
{
*(uint8_t *)(dstVRAMAddr) = 0x00;
*(uint8_t *)(dstARAMAddr) = display.screenAttr;
}
}
// Clear the shadow display scrollback RAM.
for(uint32_t dstAddr = startIdx; dstAddr < endIdx; dstAddr++)
{
display.screenCharBuf[dstAddr] = 0x20;
display.screenAttrBuf[dstAddr] = display.screenAttr;
}
return;
}
// Method to clear a line, starting at a given column.
void mzClearLine(int row, int colStart, int colEnd, uint8_t updPos)
{
// Locals.
uint8_t newRow = row;
uint8_t newColStart = colStart;
uint8_t newColEnd = colEnd;
// Adjust the parameters, -1 indicates use current position.
if(row == -1) newRow = display.displayRow;
if(colStart == -1) newColStart = 0;
if(colEnd == -1) newColEnd = display.maxScreenCol-1;
// Sanity checks.
if(newRow > display.maxDisplayRow || newColStart > display.maxScreenCol || newColEnd > display.maxScreenCol || newColEnd <= newColStart)
return;
// Clear the physical character display and attribute RAM.
uint32_t screenAttr = display.screenAttr << 24 | display.screenAttr << 16 | display.screenAttr << 8 | display.screenAttr;
uint32_t dstVRAMStartAddr = VIDEO_VRAM_BASE_ADDR+(newRow*VC_MAX_COLUMNS)+newColStart;
uint32_t dstVRAMEndAddr = dstVRAMStartAddr + newColEnd;
uint32_t dstARAMStartAddr = VIDEO_ARAM_BASE_ADDR+(newRow*VC_MAX_COLUMNS)+newColStart;
// Select 32bit or 8 bit clear depending on the start/end position.
//
if((dstVRAMStartAddr&0x3) == 0 && (dstVRAMEndAddr&0x3) ==0)
{
uint32_t screenAttr = display.screenAttr << 24 | display.screenAttr << 16 | display.screenAttr << 8 | display.screenAttr;
for(uint32_t dstVRAMAddr=dstVRAMStartAddr, dstARAMAddr = dstARAMStartAddr; dstVRAMAddr < dstVRAMEndAddr; dstVRAMAddr+=4, dstARAMAddr+=4)
{
*(uint32_t *)(dstVRAMAddr) = 0x00000000;
*(uint32_t *)(dstARAMAddr) = screenAttr;
}
} else
{
for(uint32_t dstVRAMAddr=dstVRAMStartAddr, dstARAMAddr = dstARAMStartAddr; dstVRAMAddr <= dstVRAMEndAddr; dstVRAMAddr+=1, dstARAMAddr+=1)
{
*(uint8_t *)(dstVRAMAddr) = 0x00;
*(uint8_t *)(dstARAMAddr) = display.screenAttr;
}
}
// Clear the shadow display scrollback RAM.
uint32_t startIdx = (((display.screenRow < display.maxDisplayRow ? newRow : (display.screenRow - display.maxDisplayRow + newRow))) * display.maxScreenCol) + newColStart;
for(uint32_t dstAddr = startIdx; dstAddr <= startIdx + newColEnd; dstAddr++)
{
display.screenCharBuf[dstAddr] = 0x20;
display.screenAttrBuf[dstAddr] = display.screenAttr;
}
// Update the screen pointer if needed.
if(updPos)
{
display.displayRow = newRow;
display.displayCol = newColEnd;
}
return;
}
// Method to set the VGA Border colour when running in a VGA mode where the output display doesnt match the VGA display leaving blank pixels.
uint8_t mzSetVGABorder(uint8_t vborder)
{
// Locals.
uint8_t mode = (uint8_t)*(volatile uint32_t *)(VCADDR_32BIT_VMVGATTR) & VMBORDER_MASK;
// Sanity check parameters.
if(vborder != VMBORDER_BLACK && vborder != VMBORDER_BLUE && vborder != VMBORDER_RED && vborder != VMBORDER_PURPLE && vborder != VMBORDER_GREEN && vborder != VMBORDER_CYAN && vborder != VMBORDER_YELLOW && vborder != VMBORDER_WHITE)
return(1);
// Set the VGA Border.
*(volatile uint8_t *)(VCADDR_8BIT_VMVGATTR) = mode | vborder;
return(0);
}
// Method to set the VGA mode.
uint8_t mzSetVGAMode(uint8_t vgamode)
{
// Locals.
uint8_t mode = (uint8_t)*(volatile uint32_t *)(VCADDR_32BIT_VMVGAMODE) & VMMODE_VGA_MASK;
// Sanity check parameters.
if(vgamode != VMMODE_VGA_OFF && vgamode != VMMODE_VGA_640x480 && vgamode != VMMODE_VGA_800x600)
return(1);
// Set the VGA mode.
*(volatile uint8_t *)(VCADDR_8BIT_VMVGAMODE) = mode | vgamode;
return(0);
}
// Method to set the screen mode, ie. machine video being emulated.
//
uint8_t mzSetMachineVideoMode(uint8_t vmode)
{
// Locals.
uint8_t mode = (uint8_t)*(volatile uint32_t *)(VCADDR_32BIT_VMCTRL) & VMMODE_MASK;
// Sanity check parameters.
if(vmode != VMMODE_MZ80K && vmode != VMMODE_MZ80C && vmode != VMMODE_MZ1200 && vmode != VMMODE_MZ80A && vmode != VMMODE_MZ700 && vmode != VMMODE_MZ1500 && vmode != VMMODE_MZ800 && vmode != VMMODE_MZ80B && vmode != VMMODE_MZ2000 && vmode != VMMODE_MZ2200 && vmode != VMMODE_MZ2500)
return(1);
// Set the hardware video mode.
*(volatile uint8_t *)(VCADDR_8BIT_VMCTRL) = mode | hwmode;
return(0);
}
// Method to return the character based screen width.
//
uint8_t mzGetScreenWidth(void)
{
return(display.maxScreenCol);
}
// Method to setup the character based screen width.
//
uint8_t mzSetScreenWidth(uint8_t width)
{
// Locals.
uint8_t mode = (uint8_t)*(volatile uint32_t *)(VCADDR_32BIT_VMCTRL) & VMMODE_80CHAR_MASK;
// Sanity check parameters.
if(width != 40 && width != 80)
return(1);
// Toggle the 40/80 bit according to requirements.
if(width == 40)
{
*(volatile uint8_t *)(VCADDR_8BIT_VMCTRL) = mode;
display.maxScreenCol = 40;
}
else
{
*(volatile uint8_t *)(VCADDR_8BIT_VMCTRL) = mode | VMMODE_80CHAR;
display.maxScreenCol = 80;
}
return(0);
}
// Method to refresh the screen from the scrollback buffer contents.
void mzRefreshScreen(void)
{
// Refresh the screen with buffer window contents
uint32_t startIdx = (display.screenRow < display.maxDisplayRow ? 0 : (display.screenRow - display.maxDisplayRow)+1) * display.maxScreenCol;
for(uint32_t srcIdx = startIdx, dstVRAMAddr = VIDEO_VRAM_BASE_ADDR, dstARAMAddr = VIDEO_ARAM_BASE_ADDR; srcIdx < startIdx+(display.maxDisplayRow*display.maxScreenCol); srcIdx++)
{
*(uint8_t *)(dstVRAMAddr++) = dispCodeMap[display.screenCharBuf[srcIdx]].dispCode;
*(uint8_t *)(dstARAMAddr++) = display.screenAttrBuf[srcIdx];
}
}
// Method to scroll the screen contents upwards, either because new data is being added to the bottom or for scrollback.
//
uint8_t mzScrollUp(uint8_t lines, uint8_t clear)
{
// Sanity check.
if(lines > display.maxDisplayRow)
return(1);
// Restore cursor character before scrolling.
mzFlashCursor(CURSOR_RESTORE);
// Add the lines to the current row address. If the row exceeds the maximum then scroll the screen up.
display.screenRow += lines;
display.displayRow += lines;
if(display.displayRow >= display.maxDisplayRow)
{
display.displayRow = display.maxDisplayRow - 1;
}
// At end of buffer? Shift up.
if(display.screenRow >= display.maxScreenRow)
{
uint32_t srcAddr = (lines * display.maxScreenCol);
uint32_t dstAddr = 0;
for(; srcAddr < VC_DISPLAY_BUFFER_SIZE; srcAddr++, dstAddr++)
{
display.screenCharBuf[dstAddr] = display.screenCharBuf[srcAddr];
display.screenAttrBuf[dstAddr] = display.screenAttrBuf[srcAddr];
}
for(; dstAddr < VC_DISPLAY_BUFFER_SIZE; dstAddr++)
{
display.screenCharBuf[dstAddr] = 0x20;
display.screenAttrBuf[dstAddr] = display.screenAttr;
}
display.screenRow = display.maxScreenRow-1;
}
// If we havent scrolled at the end of the buffer then clear the lines scrolled if requested.
else if(clear && display.displayRow == display.maxDisplayRow - 1)
{
uint32_t startIdx = (display.screenRow - lines + 1) * display.maxScreenCol;
uint32_t endIdx = startIdx + (lines * display.maxScreenCol);
// Clear the shadow display scrollback RAM.
for(uint32_t dstAddr = startIdx; dstAddr < endIdx; dstAddr++)
{
display.screenCharBuf[dstAddr] = 0x20;
display.screenAttrBuf[dstAddr] = display.screenAttr;
}
}
// Refresh the screen with buffer window contents
mzRefreshScreen();
return(0);
}
// Method to scroll the screen contents downwards for scrollback purposes.
uint8_t mzScrollDown(uint8_t lines)
{
// Sanity check.
if(lines > display.maxDisplayRow)
return(1);
// Restore cursor character before scrolling.
mzFlashCursor(CURSOR_RESTORE);
// Subtract the lines from the current row address. If the row falls below zero then scroll the screen down.
if((((int)display.screenRow) - lines) < 0) { display.screenRow = 0; }
else if( display.screenRow < display.maxDisplayRow ) { display.screenRow = display.maxDisplayRow -1; }
else { display.screenRow -= lines; }
// Same for the physical row pointer.
if((((int)display.displayRow) - lines) < 0) { display.displayRow = 0; }
else if( display.displayRow < display.maxDisplayRow ) { display.displayRow = display.maxDisplayRow -1; }
else { display.displayRow -= lines; }
// Refresh screen.
mzRefreshScreen();
return(0);
}
// Method to move the cursor within the phsyical screen buffer.
//
uint8_t mzMoveCursor(enum CURSOR_POSITION pos, uint8_t cnt)
{
// Process according to command.
//
switch(pos)
{
case CURSOR_UP:
display.displayRow = (int)(display.displayRow - cnt) < 0 ? 0 : display.displayRow-cnt;
break;
case CURSOR_DOWN:
display.displayRow = (int)(display.displayRow + cnt) >= display.maxDisplayRow ? display.maxDisplayRow -1 : display.displayRow+cnt;
break;
case CURSOR_LEFT:
display.displayCol = (int)(display.displayCol - cnt) < 0 ? 0 : display.displayCol-cnt;
break;
case CURSOR_RIGHT:
display.displayCol = (int)(display.displayCol + cnt) >= display.maxScreenCol ? display.maxScreenCol -1 : display.displayCol+cnt;
break;
case CURSOR_COLUMN:
if(cnt < display.maxScreenCol)
display.displayCol = cnt;
break;
case CURSOR_NEXT_LINE:
display.displayCol = 0;
if(display.displayRow < display.maxDisplayRow-1)
display.displayRow += 1;
break;
case CURSOR_PREV_LINE:
display.displayCol = 0;
if(display.displayRow > 0)
display.displayRow -= 1;
break;
default:
break;
}
return(0);
}
// Method to set the physical X/Y location of the cursor.
//
uint8_t mzSetCursor(uint8_t x, uint8_t y)
{
display.screenRow = y >= display.maxDisplayRow ? display.maxDisplayRow-1 : y;
display.displayRow = y >= display.maxDisplayRow ? display.maxDisplayRow-1 : y;
display.displayCol = x >= display.maxScreenCol ? display.maxScreenCol-1 : x;
return(0);
}
// Stream method to output a character to the display.
//
int mzPutChar(char c, FILE *stream)
{
// Variables and locals.
uint8_t output = 1;
uint32_t dispMemAddr;
// Restore character under cursor before printing.
mzFlashCursor(CURSOR_RESTORE);
// Pre-process special characters.
switch(c)
{
// Return to start of line?
case CR:
display.displayCol = 0;
output = 0;
break;
// New line?
case LF:
// Increment line and scroll if necessary.
mzScrollUp(1, 1);
display.displayCol = 0;
output = 0;
break;
// Backspace.
case BACKS:
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
output = 0;
break;
// Delete.
case DELETE:
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
mzPutChar(SPACE, stream);
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
output = 0;
break;
// Tab - expand by printing whitespace.
case TAB:
for(uint8_t idx=0; idx < 4; idx++)
mzPutChar(SPACE, stream);
output = 0;
break;
// Scroll screen up.
case SCROLL:
mzScrollUp(1, 0);
output = 0;
break;
}
// Output to screen if flag set.
if(output)
{
// Output character using default attributes.
dispMemAddr = VIDEO_VRAM_BASE_ADDR + (display.displayRow * display.maxScreenCol) + display.displayCol;
*(uint8_t *)(dispMemAddr) = (char)dispCodeMap[c].dispCode;
display.screenCharBuf[(display.screenRow * display.maxScreenCol) + display.displayCol] = c;
//
dispMemAddr = VIDEO_ARAM_BASE_ADDR + (display.displayRow * display.maxScreenCol) + display.displayCol;
*(uint8_t *)(dispMemAddr) = display.screenAttr;
display.screenAttrBuf[(display.screenRow * display.maxScreenCol) + display.displayCol] = display.screenAttr;
if(++display.displayCol >= display.maxScreenCol)
{
if(display.lineWrap)
{
// Increment line and scroll if necessary.
display.displayCol = 0;
mzScrollUp(1, 1);
} else
{
display.displayCol = display.maxScreenCol-1;
}
}
}
if(display.debug && !display.inDebug) mzDebugOut(3, c);
return(0);
}
// Stream method to output a character to the display. This method becomes the defacto output for system calls (ie. printf).
//
int mzPrintChar(char c, FILE *stream)
{
// If the ansi terminal emulator is enabled, parse the character through the emulator.
if(display.useAnsiTerm)
{
mzAnsiTerm(c);
} else
{
mzPutChar(c, stream);
}
return(0);
}
// Method to put a character onto the screen without character interpretation.
//
int mzPutRaw(char c)
{
// Variables and locals.
uint32_t dispMemAddr;
// Output character using default attributes.
dispMemAddr = VIDEO_VRAM_BASE_ADDR + (display.displayRow * display.maxScreenCol) + display.displayCol;
*(uint8_t *)(dispMemAddr) = (char)dispCodeMap[c].dispCode;
display.screenCharBuf[(display.screenRow * display.maxScreenCol) + display.displayCol] = c;
//
dispMemAddr = VIDEO_ARAM_BASE_ADDR + (display.displayRow * display.maxScreenCol) + display.displayCol;
*(uint8_t *)(dispMemAddr) = display.screenAttr;
display.screenAttrBuf[(display.screenRow * display.maxScreenCol) + display.displayCol] = display.screenAttr;
if(++display.displayCol >= display.maxScreenCol)
{
if(display.lineWrap)
{
// Increment line and scroll if necessary.
display.displayCol = 0;
mzScrollUp(1, 0);
} else
{
display.displayCol = display.maxScreenCol-1;
}
}
return(0);
}
// Method to process an ANSI ESCape Set Attribute value into a Sharp MZ attribute setting.
//
uint8_t mzSetAnsiAttribute(uint8_t attr)
{
// Process attribute command byte and set the corresponding Sharp setting.
switch(attr)
{
// Reset to default.
case 0:
display.screenAttr = VMATTR_FG_WHITE | VMATTR_BG_BLUE;
break;
// Invert FG/BG.
case 7:
// If background goes to white with default colours, it is not so readable so change foreground colour.
if((display.screenAttr & VMATTR_FG_MASKIN) == VMATTR_FG_WHITE)
{
display.screenAttr = VMATTR_FG_WHITE | VMATTR_BG_RED;
} else
{
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKIN) >> 4 | (display.screenAttr & VMATTR_BG_MASKIN) << 4;
}
break;
// Foreground black.
case 30:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_BLACK;
break;
// Foreground red.
case 31:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_RED;
break;
// Foreground green.
case 32:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_GREEN;
break;
// Foreground yellow.
case 33:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_YELLOW;
break;
// Foreground blue.
case 34:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_BLUE;
break;
// Foreground magenta.
case 35:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_PURPLE;
break;
// Foreground cyan.
case 36:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_CYAN;
break;
// Foreground white.
case 37:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_WHITE;
break;
// Default Foreground colour.
case 39:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_WHITE;
break;
// Background black.
case 40:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_BLACK;
break;
// Background red.
case 41:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_RED;
break;
// Background green.
case 42:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_GREEN;
break;
// Background yellow.
case 43:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_YELLOW;
break;
// Background blue.
case 44:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_BLUE;
break;
// Background magenta.
case 45:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_PURPLE;
break;
// Background cyan.
case 46:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_CYAN;
break;
// Background white.
case 47:
display.screenAttr = (display.screenAttr & VMATTR_BG_MASKOUT) | VMATTR_BG_WHITE;
break;
// Default Background colour.
case 49:
display.screenAttr = (display.screenAttr & VMATTR_FG_MASKOUT) | VMATTR_FG_BLUE;
break;
// Not supported.
default:
break;
}
return(0);
}
// Simple Ansi Terminal escape sequence parser. This translates ANSI Escape sequences output by programs such as the Kilo Editor
// into actual display updates.
//
int mzAnsiTerm(char c)
{
// Locals.
char *ptr;
long result;
// State machine to process the incoming character stream. Look for ANSI escape sequences and process else display the character.
switch (ansiterm.state)
{
// Looking for an ESC character, if one is found move to the next state else just process the character for output.
case ANSITERM_ESC:
default:
switch(c)
{
// Enhanced escape sequence start.
case ESC:
ansiterm.charcnt = 0;
ansiterm.paramcnt = 0;
ansiterm.setScreenMode = 0;
ansiterm.setExtendedMode = 0;
ansiterm.state = ANSITERM_BRACKET;
break;
// Return to start of line?
case CR:
display.displayCol = 0;
break;
// New line?
case LF:
// Increment line and scroll if necessary.
mzScrollUp(1, 1);
display.displayCol = 0;
break;
// Backspace.
case BACKS:
mzFlashCursor(CURSOR_RESTORE);
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
break;
// Delete.
case DELETE:
mzFlashCursor(CURSOR_RESTORE);
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
mzPutRaw(SPACE);
display.displayCol = display.displayCol == 0 ? 0 : display.displayCol -1;
break;
// Tab - expand by printing whitespace.
case TAB:
mzFlashCursor(CURSOR_RESTORE);
for(uint8_t idx=0; idx < 4; idx++)
mzPutRaw(SPACE);
break;
default:
mzPutRaw(c);
break;
}
break;
// Escape found, no look for next in the ESCape sequence or abort the state if a character is received which is not expected, print the character.
case ANSITERM_BRACKET:
switch(c)
{
case ESC:
break;
case '[':
ansiterm.state = ANSITERM_PARSE;
break;
case '7':
// Save the current cursor position.
ansiterm.saveRow = display.displayRow;
ansiterm.saveCol = display.displayCol;
ansiterm.saveScreenRow = display.screenRow;
ansiterm.state = ANSITERM_ESC;
break;
case '8':
// Restore the current cursor position.
display.displayRow = ansiterm.saveRow;
display.displayCol = ansiterm.saveCol;
display.screenRow = ansiterm.saveScreenRow;
ansiterm.state = ANSITERM_ESC;
break;
default:
ansiterm.state = ANSITERM_ESC;
mzPutRaw(c);
break;
}
break;
// Parse the ESCAPE sequence. If digits come in then these are parameters so store and parse into the parameter array. When a terminating command is received, execute the desired command.
case ANSITERM_PARSE:
// Multiple Escapes, or incomplete sequences. If an escape occurs then go back to looking for a bracket or similar as we are on a
// different escape sequence.
if(c == ESC)
{
ansiterm.state = ANSITERM_BRACKET;
}
else if(isdigit(c))
{
ansiterm.charbuf[ansiterm.charcnt++] = c;
ansiterm.charbuf[ansiterm.charcnt] = 0x00;
}
else if(c == ';')
{
ptr = (char *)&ansiterm.charbuf;
if(!xatoi(&ptr, &result))
{
ansiterm.state = ANSITERM_ESC;
} else
{
ansiterm.param[ansiterm.paramcnt++] = (uint16_t)result;
}
ansiterm.charcnt = 0;
}
else if(c == '=')
{
ansiterm.setScreenMode = 1;
}
else if(c == '?')
{
ansiterm.setExtendedMode = 1;
}
else
{
// No semicolon so attempt to get the next parameter before processing command.
if(ansiterm.charcnt > 0)
{
ptr = (char *)&ansiterm.charbuf;
if(xatoi(&ptr, &result))
{
ansiterm.param[ansiterm.paramcnt++] = (uint16_t)result;
}
}
// Process the command now parameters have been retrieved.
switch(c)
{
// Position cursor.
case 'H':
// Set the cursor to given co-ordinates.
if(ansiterm.paramcnt >= 2)
{
mzSetCursor((uint8_t)ansiterm.param[1]-1, (uint8_t)ansiterm.param[0]-1);
}
// Home cursor.
else if(ansiterm.paramcnt == 0)
{
mzSetCursor(0, 0);
}
break;
// Move cursor up.
case 'A':
if(ansiterm.paramcnt > 0)
{
mzMoveCursor(CURSOR_UP, (uint8_t)ansiterm.param[0]);
} else
{
mzMoveCursor(CURSOR_UP, 1);
}
break;
// Move cursor down.
case 'B':
if(ansiterm.paramcnt > 0)
{
mzMoveCursor(CURSOR_DOWN, (uint8_t)ansiterm.param[0]);
} else
{
mzMoveCursor(CURSOR_DOWN, 1);
}
break;
// Move cursor right.
case 'C':
if(ansiterm.paramcnt > 0)
{
mzMoveCursor(CURSOR_RIGHT, (uint8_t)ansiterm.param[0]);
} else
{
mzMoveCursor(CURSOR_RIGHT, 1);
}
break;
// Move cursor left.
case 'D':
if(ansiterm.paramcnt > 0)
{
mzMoveCursor(CURSOR_LEFT, (uint8_t)ansiterm.param[0]);
} else
{
mzMoveCursor(CURSOR_LEFT, 1);
}
break;
// Move cursor to start of next line.
case 'E':
mzMoveCursor(CURSOR_NEXT_LINE, 0);
break;
// Move cursor to start of previous line.
case 'F':
mzMoveCursor(CURSOR_PREV_LINE, 0);
break;
// Move cursor to absolute column position.
case 'G':
if(ansiterm.paramcnt > 0)
{
mzMoveCursor(CURSOR_COLUMN, (uint8_t)ansiterm.param[0]-1);
} else
{
mzMoveCursor(CURSOR_COLUMN, 0);
}
break;
// Scroll up.
case 'S':
if(ansiterm.paramcnt > 0)
{
mzScrollUp((uint8_t)ansiterm.param[0], 0);
} else
{
mzScrollUp(1, 0);
}
break;
// Scroll down.
case 'T':
if(ansiterm.paramcnt > 0)
{
mzScrollDown((uint8_t)ansiterm.param[0]);
} else
{
mzScrollDown(1);
}
break;
case 'R':
printf("Report Cursor:");
for(uint8_t idx=0; idx < ansiterm.paramcnt; idx++)
printf("%d,", ansiterm.param[idx]);
printf("\n");
break;
case 's':
// Save the current cursor position.
ansiterm.saveRow = display.displayRow;
ansiterm.saveCol = display.displayCol;
ansiterm.saveScreenRow = display.screenRow;
break;
case 'u':
// Restore the current cursor position.
display.displayRow = ansiterm.saveRow;
display.displayCol = ansiterm.saveCol;
display.screenRow = ansiterm.saveScreenRow;
break;
// Report data.
case 'n': {
char response[MAX_KEYB_BUFFER_SIZE];
// Report current cursor position?
//
if(ansiterm.paramcnt > 0 && ansiterm.param[0] == 6)
{
// Build response and push onto keyboard buffer.
sprintf(response, "%c[%d;%dR", ESC, display.displayRow+1, display.displayCol+1);
mzPushKey(response);
}
} break;
// Clear Screen or block of screen.
case 'J': {
// Default is to clear the complete display but not scrollback buffer.
int clearMode = 2;
if(ansiterm.paramcnt > 0 && ansiterm.param[0] < 4)
{
clearMode = ansiterm.param[0];
}
mzClearScreen(clearMode, 1);
} break;
// Clear line.
case 'K': {
int clearRow = -1;
int clearColStart = 0;
int clearColEnd = display.maxScreenCol-1;
if(ansiterm.paramcnt > 0)
{
// 0 = clear cursor to end of line.
if(ansiterm.param[0] == 0)
{
clearColStart = display.displayCol;
}
// 1 = clear from beginning to cursor.
else if(ansiterm.param[0] == 1)
{
clearColEnd = display.displayCol;
}
// 2 = clear whole line.
}
mzClearLine(clearRow, clearColStart, clearColEnd, 0);
} break;
// Set Display Attributes.
case 'm':
// Process all the attributes.
for(uint8_t idx=0; idx < ansiterm.paramcnt; idx++)
{
mzSetAnsiAttribute(ansiterm.param[idx]);
}
break;
case 'h':
// Show cursor?
if(ansiterm.paramcnt > 0 && ansiterm.param[0] == 25)
{
mzFlashCursor(CURSOR_ON);
}
break;
case 'l':
// Hide cursor?
if(ansiterm.paramcnt > 0 && ansiterm.param[0] == 25)
{
mzFlashCursor(CURSOR_OFF);
}
break;
default:
mzPutRaw(c);
ansiterm.state = ANSITERM_ESC;
break;
}
ansiterm.state = ANSITERM_ESC;
if(display.debug && !display.inDebug) mzDebugOut(1, c);
}
break;
}
return(0) ;
}
// Method to output debug data to track issues with the display or ansi emulator.
//
void mzDebugOut(uint8_t set, uint8_t data1)
{
// Save current coordinates.
uint8_t sr = display.displayRow;
uint8_t scr= display.screenRow;
uint8_t sc = display.displayCol;
uint8_t uat= display.useAnsiTerm;
// Disable ansi terminal so we dont get recursion issues when we call printf. Disable further debug calls whilst data
// is being output.
display.useAnsiTerm = 0;
display.inDebug = 1;
switch(set)
{
// Debug data to show the escape sequence and parameters.
case 1: {
// Set location where debug data should be displayed.
display.displayRow = 0;
display.displayCol = 40;
display.screenRow = 0;
// Output required data.
printf("D:%d-%d-%d:%c:%d,%d,%d:", sr,sc,scr,data1,ansiterm.paramcnt, ansiterm.setScreenMode, ansiterm.setExtendedMode);
for(uint8_t idx=0; idx < ansiterm.paramcnt; idx++)
printf("%d,", ansiterm.param[idx]);
printf(" ");
// Set a delay so that the change can be seen.
//TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 50);
} break;
case 2: {
// Set location where debug data should be displayed.
display.displayRow = 1;
display.displayCol = 40;
display.screenRow = 1;
printf("K:%d:", strlen(keyboard.keyBuf));
for(uint8_t idx=0; idx < strlen(keyboard.keyBuf); idx++)
printf("%02x,", keyboard.keyBuf[idx]);
// Set a delay so that the change can be seen.
//TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 100);
} break;
case 3: {
// Set location where debug data should be displayed.
display.displayRow = 2;
display.displayCol = 40;
display.screenRow = 2;
printf("X:%d,%d,%d,%d,%d,%d:%02x", sr, sc, scr, display.maxScreenRow, display.maxDisplayRow, display.maxScreenCol, data1);
// Set a delay so that the change can be seen.
//TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 1000);
} break;
// No set defined, illegal call!
default:
break;
}
// Restore ansi emulation mode and disable further debug calls.
display.useAnsiTerm = uat;
display.inDebug = 0;
// Restore coordinates
display.displayRow = sr;
display.screenRow = scr;
display.displayCol = sc;
return;
}
// Method to flash a cursor at current X/Y location on the physical screen.
uint8_t mzFlashCursor(enum CURSOR_STATES state)
{
// Locals.
uint32_t dispMemAddr = VIDEO_VRAM_BASE_ADDR + (display.displayRow * display.maxScreenCol) + display.displayCol;
uint16_t srcIdx = (display.screenRow * display.maxScreenCol) + display.displayCol;
// Action according to request.
switch(state)
{
// Disable the cursor flash mechanism.
case CURSOR_OFF:
default:
// Only restore character if it had been previously saved and active.
if(keyboard.cursorOn == 1 && keyboard.displayCursor == 1)
{
*(uint8_t *)(dispMemAddr) = dispCodeMap[display.screenCharBuf[srcIdx]].dispCode;
}
keyboard.cursorOn = 0;
break;
// Enable the cursor mechanism.
case CURSOR_ON:
keyboard.cursorOn = 1;
break;
// Restore the character under the cursor.
case CURSOR_RESTORE:
if(keyboard.displayCursor == 1)
{
*(uint8_t *)(dispMemAddr) = dispCodeMap[display.screenCharBuf[srcIdx]].dispCode;
}
break;
// If enabled and timer expired, flash cursor.
case CURSOR_FLASH:
if(keyboard.cursorOn == 1 && (keyboard.flashTimer == 0L || keyboard.flashTimer + KEYB_FLASH_TIME < RTC_MILLISECONDS_EPOCH))
{
keyboard.displayCursor = keyboard.displayCursor == 1 ? 0 : 1;
keyboard.flashTimer = RTC_MILLISECONDS_EPOCH;
if(keyboard.displayCursor == 1)
{
switch(keyboard.mode)
{
case KEYB_LOWERCASE:
*(uint8_t *)(dispMemAddr) = CURSOR_UNDERLINE;
break;
case KEYB_CAPSLOCK:
*(uint8_t *)(dispMemAddr) = CURSOR_BLOCK;
break;
case KEYB_SHIFTLOCK:
default:
*(uint8_t *)(dispMemAddr) = CURSOR_THICK_BLOCK;
break;
}
} else
{
*(uint8_t *)(dispMemAddr) = dispCodeMap[display.screenCharBuf[srcIdx]].dispCode;
}
}
break;
}
return(0);
}
// A method to push keys into the keyboard buffer as though they had been pressed. This
// is necessary for the ANSI Terminal emulation but also a useful feature for applications.
//
uint8_t mzPushKey(char *keySeq)
{
uint8_t seqSize = strlen(keySeq);
// Sanity check, cant push more keys then the keyboard buffer will hold.
//
if((keyboard.keyBufPtr + seqSize) > MAX_KEYB_BUFFER_SIZE)
return(1);
// Concat the key sequence into the keyboard buffer.
//
strcat(keyboard.keyBuf, keySeq);
return(0);
}
// Method to sweep the keyboard and store any active keys. Detect key down, key up and keys being held.
uint8_t mzSweepKeys(void)
{
// Sequence through the strobe lines and read back the scan data into the buffer.
for(uint8_t strobe=0xF0; strobe < 0xFA; strobe++)
{
// Output the keyboard strobe.
*(volatile uint8_t *)(MBADDR_8BIT_KEYPA) = strobe;
// Slight delay to allow for bounce.
TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 1);
// Read the scan lines.
keyboard.scanbuf[0][strobe-0xF0] = (uint8_t)(*(volatile uint32_t *)(MBADDR_8BIT_KEYPB));
// keyboard.scanbuf[0][strobe-0xF0] = (uint8_t)(*(volatile uint32_t *)(MBADDR_8BIT_KEYPB));
//printf("R%02x ", (uint8_t)(*(volatile uint32_t *)(MBADDR_8BIT_KEYPB)));
}
// Now look for active keys.
for(uint8_t strobeIdx=0; strobeIdx < 10; strobeIdx++)
{
// Skip over modifier keys.
//if(strobeIdx == 8) continue;
if(keyboard.scanbuf[0][strobeIdx] != keyboard.scanbuf[1][strobeIdx])
{
if(keyboard.scanbuf[0][strobeIdx] != 0xFF)
{
keyboard.keydown[strobeIdx] = keyboard.scanbuf[0][strobeIdx];
} else
{
keyboard.keydown[strobeIdx] = 0xFF;
}
if(keyboard.scanbuf[1][strobeIdx] != 0xFF)
{
keyboard.keyup[strobeIdx] = keyboard.scanbuf[1][strobeIdx];
} else
{
keyboard.keyup[strobeIdx] = 0xFF;
}
} else
{
if(keyboard.scanbuf[0][strobeIdx] != 0xFF)
{
keyboard.keyhold[strobeIdx]++;
} else
{
keyboard.keyhold[strobeIdx] = 0;
keyboard.keydown[strobeIdx] = 0xFF;
keyboard.keyup[strobeIdx] = 0xFF;
}
}
keyboard.scanbuf[1][strobeIdx] = keyboard.scanbuf[0][strobeIdx];
}
//printf("\n");
// Check for modifiers.
//
if((keyboard.scanbuf[0][8] & 0x80) == 0)
{
keyboard.breakKey = 1;
} else
{
keyboard.breakKey = 0;
}
if((keyboard.scanbuf[0][8] & 0x40) == 0)
{
keyboard.ctrlKey = 1;
} else
{
keyboard.ctrlKey = 0;
}
if((keyboard.scanbuf[0][8] & 0x01) == 0)
{
keyboard.shiftKey = 1;
} else
{
keyboard.shiftKey = 0;
}
return(0);
}
// Method to scan the keyboard and return any valid key presses.
// Input: mode = 0 - No blocking, standard keyboard.
// 1 - blocking, standard keyboard.
// 2 - No blocking, ansi keyboard.
// 3 - blocking, ansi keyboard.
// Return: -1 = no key pressed.
// ASCII value when key pressed.
int mzGetKey(uint8_t mode)
{
// Locals.
int retcode = -1;
// Return buffered key strokes first, once exhausted scan for more.
//
if(keyboard.keyBuf[keyboard.keyBufPtr] != 0x00)
{
retcode = (int)keyboard.keyBuf[keyboard.keyBufPtr++];
}
else
{
// Loop if blocking flag set until a key is pressed else get a key if available and return.
do {
// Flash the cursor as needed.
mzFlashCursor(CURSOR_FLASH);
// Make a sweep of the keyboard, updating the key map.
mzSweepKeys();
// Run through the strobe sequence and identify any pressed keys, mapping to an ASCII value for return.
for(uint8_t strobeIdx=0; strobeIdx < 10; strobeIdx++)
{
// Skip over modifier keys.
//if(strobeIdx == 8) continue;
// If a keyboard press is released, cancel autorepeat.
if((keyboard.keydown[strobeIdx] != 0xFF && keyboard.keyhold[strobeIdx] == 0) || (keyboard.keyup[strobeIdx] != 0xFF && keyboard.keyhold[strobeIdx] > 0))
{
keyboard.autorepeat = 0;
} else
if(keyboard.keydown[strobeIdx] != 0xFF && keyboard.keyhold[strobeIdx] == 1)
{
uint8_t keyIdx = 0;
uint8_t key = keyboard.keydown[strobeIdx];
uint8_t modifiedMode = keyboard.ctrlKey == 1 ? KEYB_CTRL : keyboard.mode == KEYB_LOWERCASE && keyboard.shiftKey == 1 ? KEYB_SHIFTLOCK : keyboard.mode == KEYB_SHIFTLOCK && keyboard.shiftKey == 1 ? KEYB_CAPSLOCK : keyboard.mode == KEYB_CAPSLOCK && keyboard.shiftKey == 1 ? KEYB_LOWERCASE : keyboard.mode;
for(; keyIdx < 8 && key & 0x80; keyIdx++, key=key << 1);
retcode = scanCodeMap[modifiedMode].scanCode[(strobeIdx*8)+keyIdx];
// Setup auto repeat.
keyboard.repeatKey = retcode;
keyboard.holdTimer = RTC_MILLISECONDS_EPOCH;
} else
if(keyboard.keydown[strobeIdx] != 0xFF && keyboard.keyhold[strobeIdx] > 1 && keyboard.holdTimer + KEYB_AUTOREPEAT_INITIAL_TIME < RTC_MILLISECONDS_EPOCH)
{
keyboard.autorepeat = 1;
keyboard.holdTimer = RTC_MILLISECONDS_EPOCH;
} else
if(keyboard.keydown[strobeIdx] != 0xFF && keyboard.keyhold[strobeIdx] > 1 && keyboard.autorepeat == 1 && keyboard.holdTimer + KEYB_AUTOREPEAT_TIME < RTC_MILLISECONDS_EPOCH)
{
keyboard.holdTimer = RTC_MILLISECONDS_EPOCH;
retcode = keyboard.repeatKey;
}
}
// Process internal keys, dont return.
//
switch(retcode)
{
// Toggle through the 3 key locks, lowercase, shiftlock, capslock.
case ALPHAKEY:
keyboard.mode = keyboard.mode == KEYB_LOWERCASE ? KEYB_SHIFTLOCK : keyboard.mode == KEYB_SHIFTLOCK ? KEYB_CAPSLOCK : KEYB_LOWERCASE;
retcode = -1;
break;
// Switch to graphics mode character set.
case GRAPHKEY:
keyboard.mode = keyboard.mode == KEYB_GRAPHMODE ? KEYB_CAPSLOCK : KEYB_GRAPHMODE;
retcode = -1;
break;
// If the debug key is pressed, toggle the debugging output on/off.
case DEBUGKEY:
display.debug = (display.debug == 0 ? 1 : 0);
retcode = -1;
break;
// If the Ansi Toggle Key is pressed, toggle enabling of the Ansi Terminal Emulator (1), or raw Sharp (0).
case ANSITGLKEY:
display.useAnsiTerm = (display.useAnsiTerm == 0 ? 1 : 0);
retcode = -1;
break;
// Send cursor to 0,0.
case CURHOMEKEY:
mzSetCursor(0,0);
retcode = -1;
break;
// Clear screen.
case CLRKEY:
mzClearScreen(3, 1);
retcode = -1;
// No key assigned.
case NOKEY:
retcode = -1;
default:
break;
}
} while(retcode == -1 && (mode == 1 || mode == 3));
// If we are in ANSI terminal mode, certain keys need expansion into ANSI ESCape sequences, detect the key and expand as necessary.
//
if((display.useAnsiTerm == 1 || mode == 2 || mode == 3) && retcode != -1)
{
for(uint8_t idx=0; idx < (sizeof(ansiKeySeq)/sizeof(*ansiKeySeq)); idx++)
{
// On match, copy the escape sequence into the keyboard buffer and set pointer to start of buffer+1.
if(ansiKeySeq[idx].key == retcode)
{
strcpy(keyboard.keyBuf, ansiKeySeq[idx].ansiKeySequence);
keyboard.keyBufPtr = 0;
retcode=(int)keyboard.keyBuf[keyboard.keyBufPtr++];
break;
}
}
if(display.debug && !display.inDebug) mzDebugOut(2, retcode);
}
}
return(retcode);
}
// File stream method to get a key from the keyboard.
//
int mzGetChar(FILE *stream)
{
return(mzGetKey(1));
}
// A helper method to wait for a change in the service request status with timeout.
int mzSDGetStatus(uint32_t timeout, uint8_t initStatus)
{
uint32_t timeoutMS = RTC_MILLISECONDS_EPOCH;
uint8_t result;
// Wait for the status to update, dont read status continuously as a transaction can delay the K64F from requesting the bus to make an update.
do {
for(uint32_t timeMS = RTC_MILLISECONDS_EPOCH; timeMS == RTC_MILLISECONDS_EPOCH;);
result = svcControl->result;
} while(timeoutMS + timeout > RTC_MILLISECONDS_EPOCH && initStatus == result);
return(timeoutMS + timeout > RTC_MILLISECONDS ? (int)result : -1);
}
// Method to make a generic service call to the K64F processor.
//
int mzServiceCall(uint8_t cmd)
{
// Locals.
uint8_t retries;
uint8_t result = 0;
int status;
// Place command to be executed in control structure.
svcControl->cmd = cmd;
// Retry the command a number of times when failure occurs to allow for the I/O processor being busy.
retries = TZSVC_RETRY_COUNT;
do {
// Instigate a service request
svcControl->result = TZSVC_STATUS_REQUEST;
*(volatile uint8_t *)(MBADDR_8BIT_IOW_SVCREQ) = 0x00;
// Error occurred?
if((status=mzSDGetStatus(TZSVC_TIMEOUT, TZSVC_STATUS_REQUEST)) != -1)
{
status=mzSDGetStatus(TZSVC_TIMEOUT, TZSVC_STATUS_PROCESSING);
}
retries--;
} while(retries > 0 && (uint8_t)status != TZSVC_STATUS_OK);
return(retries == 0 ? -1 : status);
}
// Method to make a service call to the K64F processor. Actual data copy in/out is made outside this method as it only takes care of actually
// making the command request and getting the status back.
//
int mzSDServiceCall(uint8_t drive, uint8_t cmd)
{
// Locals.
int status;
// Setup control structure to request a disk sector from the I/O processor.
svcControl->fileSector = drive;
// Make the call.
status = mzServiceCall(cmd);
return(status);
}
// Method to initialise an SD card hosted on the I/O processor. This is accomplished by a service request to the I/O processor
// and it takes care of the initialisation.
//
uint8_t mzSDInit(uint8_t drive)
{
// Locals.
uint8_t result = 0;
int status;
// Make the initialisation service call.
status = mzSDServiceCall(drive, TZSVC_CMD_SD_DISKINIT);
// Did we get a successful service?
if(status == -1 || (uint8_t)status != TZSVC_STATUS_OK)
{
result = 1;
}
return(result);
}
// Method to read a sector from the SD card hosted on the I/O processor. This is accomplished by a service request to the I/O processor
// and it will read the required sector and place it into the service control record.
//
uint8_t mzSDRead(uint8_t drive, uint32_t sector, uint32_t buffer)
{
// Locals.
uint8_t result = 0;
int status;
// Setup control structure to request a disk sector from the I/O processor.
svcControl->sectorLBA = convBigToLittleEndian(sector);
// Make the disk read service call.
status = mzSDServiceCall(drive, TZSVC_CMD_SD_READSECTOR);
// Did we get a successful service?
if((uint8_t)status == TZSVC_STATUS_OK)
{
// Copy the received sector into the provided buffer.
for(uint32_t srcAddr=(uint32_t)svcControl->sector, dstAddr=buffer; dstAddr < buffer+TZSVC_SECTOR_SIZE; srcAddr++, dstAddr++)
{
*(uint8_t *)(dstAddr) = *(uint8_t *)srcAddr;
}
} else
{
result = 1;
}
return(result);
}
// Method to write a sector to the SD card hosted on the I/O processor. This is accomplished by placing data into the service control record
// and making a service request to the I/O processor which will write the data to the required sector.
//
uint8_t mzSDWrite(uint8_t drive, uint32_t sector, uint32_t buffer)
{
// Locals.
uint8_t result = 0;
int status;
// Setup control structure to request writing of a sector to a disk on the I/O processor.
svcControl->sectorLBA = convBigToLittleEndian(sector);
// Copy the provided buffer into service control record sector buffer.
for(uint32_t srcAddr=buffer, dstAddr=(uint32_t)svcControl->sector; srcAddr < buffer+TZSVC_SECTOR_SIZE; srcAddr++, dstAddr++)
{
*(uint8_t *)(dstAddr) = *(uint8_t *)srcAddr;
}
// Make the disk write service call.
status = mzSDServiceCall(drive, TZSVC_CMD_SD_WRITESECTOR);
// Did we get a successful service?
if((uint8_t)status != TZSVC_STATUS_OK)
{
result = 1;
}
return(result);
}
// Method to exit from zOS/ZPU CPU and return control to the host Z80 CPU.
//
void mzSetZ80(void)
{
// We could write direct to the CPU selection port but the host ROM may well be required as the ZPU uses it's memory. For this reason
// we make a call to the I/O processor which will load the rom, switch CPU and perform a reset.
mzServiceCall(TZSVC_CMD_CPU_SETZ80);
// Loop forever waiting for the CPU switch or a reset.
while(1);
}
//////////////////////////////////////////////////////////////
// End of Sharp MZ i/f methods for zOS //
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Temporary test routines //
//////////////////////////////////////////////////////////////
// Dummy function used for testing, it's contents change over time as tests are made on various hardware components.
int mzGetTest()
{
uint8_t nl = 0;
while(1)
{
mzSweepKeys();
TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 250);
//mzSweepKeys();
for(uint8_t strobeIdx=0; strobeIdx < 9; strobeIdx++)
{
if(keyboard.keyup[strobeIdx] != 0xFF)
{
uint8_t keyIdx = 0;
uint8_t key = keyboard.keyup[strobeIdx];
for(; keyIdx < 8 && key & 0x80; keyIdx++, key=key << 1);
printf("Up:%02x %02x", keyboard.keyup[strobeIdx], scanCodeMap[0].scanCode[(strobeIdx*8)+keyIdx]);
nl = 1;
}
if(keyboard.keydown[strobeIdx] != 0xFF)
{
uint8_t keyIdx = 0;
uint8_t key = keyboard.keydown[strobeIdx];
for(; keyIdx < 8 && key & 0x80; keyIdx++, key=key << 1);
printf("Dw:%02x %02x", keyboard.keyup[strobeIdx], scanCodeMap[0].scanCode[(strobeIdx*8)+keyIdx]);
nl = 1;
}
if(keyboard.keyhold[strobeIdx] != 0)
{
printf("Hd:%02x ", keyboard.keyhold[strobeIdx]);
nl = 1;
}
if(nl == 1)
{
printf("\n");
nl = 0;
}
}
// TIMER_MILLISECONDS_UP = 0; while(TIMER_MILLISECONDS_UP < 1000);
}
return(-1);
}
//// Test routine. Add code here to test an item within the kernel. Anything accessing hardware generally has to call the kernel as it doesnt have real access.
//
void testRoutine(void)
{
// Locals.
//
printf("No test defined.\n");
}
#endif // Protected methods which reside in the kernel.
#if defined __APP__
#endif // __APP__
#ifdef __cplusplus
}
#endif