├── emubase ├── stdafx.h ├── Defines.h ├── Memory.h ├── Emubase.h ├── Board.h ├── Hard.cpp ├── Disasm.cpp ├── Processor.h └── Floppy.cpp ├── .gitignore ├── keyboard.png ├── uknc_rom.bin ├── !compile.cmd ├── stdafx.h ├── README.md ├── shell_minimal.html ├── LICENSE ├── miniz └── zip.h ├── Emulator.cpp └── index.html /emubase/stdafx.h: -------------------------------------------------------------------------------- 1 | #include "../stdafx.h" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /emul.wasm 2 | /emul.js 3 | /emul.html 4 | -------------------------------------------------------------------------------- /keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nzeemin/ukncbtl-wasm/HEAD/keyboard.png -------------------------------------------------------------------------------- /uknc_rom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nzeemin/ukncbtl-wasm/HEAD/uknc_rom.bin -------------------------------------------------------------------------------- /!compile.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | SET EMSDKPATH=C:\bin\emsdk 4 | 5 | CALL %EMSDKPATH%\emsdk_env.bat 6 | 7 | SET SOURCE=Emulator.cpp ^ 8 | emubase\Disasm.cpp emubase\Board.cpp emubase\Processor.cpp emubase\Memory.cpp emubase\Floppy.cpp emubase\Hard.cpp ^ 9 | miniz\zip.c 10 | 11 | @echo on 12 | 13 | @rem emcc %SOURCE% -s WASM=1 2>emcc.log 14 | @rem emcc %SOURCE% -s WASM=1 -s SAFE_HEAP=1 -o emul.html --shell-file shell_minimal.html 15 | emcc %SOURCE% -s WASM=1 -O2 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s FORCE_FILESYSTEM=1 -s NO_EXIT_RUNTIME=1 -fno-exceptions -fno-rtti -o emul.html --shell-file shell_minimal.html 16 | -------------------------------------------------------------------------------- /stdafx.h: -------------------------------------------------------------------------------- 1 | 2 | #define PRODUCT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef unsigned char uint8_t; 10 | typedef unsigned short int uint16_t; 11 | typedef signed short int int16_t; 12 | typedef unsigned int uint32_t; 13 | typedef signed int int32_t; 14 | typedef unsigned long long uint64_t; 15 | 16 | 17 | #define MAKELONG(a, b) ((uint16_t)(((uint16_t)(((uint32_t)(a)) & 0xffff)) | ((uint32_t)((uint16_t)(((uint32_t)(b)) & 0xffff))) << 16)) 18 | #define MAKEWORD(a, b) ((uint16_t)(((uint8_t)(((uint32_t)(a)) & 0xff)) | ((uint16_t)((uint8_t)(((uint32_t)(b)) & 0xff))) << 8)) 19 | 20 | 21 | typedef char TCHAR; 22 | #define _T(x) x 23 | typedef const char * LPCTSTR; 24 | #define _tfopen fopen 25 | #define _tfsopen _fsopen 26 | #define _tcscpy strcpy 27 | #define _tstat _stat 28 | #define _tcsrchr strrchr 29 | #define _tcsicmp strcasecmp 30 | #define _sntprintf snprintf 31 | 32 | 33 | #define CALLBACK 34 | 35 | 36 | #define ASSERT(f) ((void)0) 37 | #define VERIFY(f) ((void)f) 38 | 39 | 40 | // DebugPrint and DebugLog 41 | void DebugPrint(LPCTSTR message); 42 | void DebugPrintFormat(LPCTSTR pszFormat, ...); 43 | void DebugLogClear(); 44 | void DebugLogCloseFile(); 45 | void DebugLog(LPCTSTR message); 46 | void DebugLogFormat(LPCTSTR pszFormat, ...); 47 | 48 | 49 | // Processor register names 50 | const LPCTSTR REGISTER_NAME[] = { _T("R0"), _T("R1"), _T("R2"), _T("R3"), _T("R4"), _T("R5"), _T("SP"), _T("PC") }; 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ukncbtl-wasm 2 | UKNCBTL — UKNC Back to Life! emulator, WASM version. 3 | 4 | *UKNCBTL* is cross-platform UKNC emulator for Windows/Linux/Mac OS X. 5 | *UKNC* (*УКНЦ*, Электроника МС-0511) is soviet school computer based on two PDP-11 compatible processors KM1801VM2. 6 | 7 | This is UKNCBTL emulator version for WebAssembly (WASM) to compile with Emscripten and to run in any modern browser. 8 | 9 | Take a look at the emulator here: 10 | https://nzeemin.github.io/ukncbtl-online.html 11 | 12 | ### Emulator files 13 | This is the files needed to run the emulator, the following files are the result of the compilation, plus the static keyboard image: 14 | * `emul.js` 15 | * `emul.wasm` 16 | * `emul.html` 17 | * `index.html` 18 | * `keyboard.png` 19 | 20 | To make it work you have to put the files on web server; WebAssembly will not work just from a file opened in a browser. 21 | 22 | ### Emulator URL parameters 23 | The emulator recognizes and uses the following (optional) URL parameters: 24 | * `state=URL` — load saved emulator state from the URL and apply it 25 | * `diskN=URL` — load disk image from the URL and attach it; `N`=0..3 26 | * `run=1` — run the emulator 27 | * `color=P` — use palette `P`=1..3, or `rgb`/`grey`/`grb` 28 | 29 | Note that the URLs are to download files from the Web by JavaScript code, so that's under restriction of Cross-Origin Resource Sharing (CORS) policy defined on your server. 30 | 31 | ----- 32 | The UKNCBTL project consists of: 33 | * [ukncbtl](https://github.com/nzeemin/ukncbtl/) written for Win32 and works under Windows 2000/2003/2008/XP/Vista/7/8/10. 34 | * [ukncbtl-renders](https://github.com/nzeemin/ukncbtl-renders/) — renderers for UKNCBTL Win32. 35 | * [ukncbtl-qt](https://github.com/nzeemin/ukncbtl-qt/) is based on Qt framework and works under Windows, Linux and Mac OS X. 36 | * [ukncbtl-testbench](https://github.com/nzeemin/ukncbtl-testbench/) — test bench for regression and performance testing. 37 | * [ukncbtl-utils](https://github.com/nzeemin/ukncbtl-utils/) — various utilities: rt11dsk, sav2wav, ESCParser etc. 38 | * [ukncbtl-doc](https://github.com/nzeemin/ukncbtl-doc/) — documentation and screenshots. 39 | * [ukncbtl-wasm](https://github.com/nzeemin/ukncbtl-wasm/) — web version of the emulator. 40 | * Project wiki: https://github.com/nzeemin/ukncbtl-doc/wiki 41 | * Screenshots: https://github.com/nzeemin/ukncbtl-doc/wiki/Screenshots-ru 42 | * User's Manual (in Russian): https://github.com/nzeemin/ukncbtl-doc/wiki/Users-Manual-ru 43 | -------------------------------------------------------------------------------- /emubase/Defines.h: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Defines.h PDP11-like processor defines 12 | 13 | #pragma once 14 | 15 | 16 | ////////////////////////////////////////////////////////////////////// 17 | 18 | inline uint8_t GetDigit(uint16_t word, int pos) 19 | { 20 | //return (word >>= pos * 3) % 8; 21 | return (uint8_t)((word >>= ((pos << 1) + pos)) & 7); 22 | } 23 | 24 | // Constants for "pos" argument 25 | #define SRC 2 26 | #define DST 0 27 | 28 | 29 | ////////////////////////////////////////////////////////////////////// 30 | 31 | // Interrupts 32 | #define NO_INTERRUPT 0000 33 | #define INTERRUPT_4 0004 34 | #define INTERRUPT_10 0010 35 | #define INTERRUPT_14 0014 36 | #define INTERRUPT_20 0020 37 | #define INTERRUPT_24 0024 38 | #define INTERRUPT_30 0030 39 | #define INTERRUPT_34 0034 40 | #define INTERRUPT_60 0060 41 | #define INTERRUPT_100 0100 42 | #define INTERRUPT_274 0274 43 | 44 | // Process Status Word (PSW) bits 45 | #define PSW_C 1 // Carry 46 | #define PSW_V 2 // Arithmetic overflow 47 | #define PSW_Z 4 // Zero result 48 | #define PSW_N 8 // Negative result 49 | #define PSW_T 16 // Trap/Debug 50 | #define PSW_P 0200 // Priority 51 | #define PSW_HALT 0400 // Halt 52 | 53 | // Commands -- no operands 54 | #define PI_HALT 0000000 55 | #define PI_WAIT 0000001 56 | #define PI_RTI 0000002 57 | #define PI_BPT 0000003 58 | #define PI_IOT 0000004 59 | #define PI_RESET 0000005 60 | #define PI_RTT 0000006 61 | #define PI_MFPT 0000007 62 | #define PI_HALT10 0000010 63 | #define PI_HALT11 0000011 64 | #define PI_HALT12 0000012 65 | #define PI_HALT13 0000013 66 | #define PI_HALT14 0000014 67 | #define PI_HALT15 0000015 68 | #define PI_HALT16 0000016 69 | #define PI_HALT17 0000017 70 | #define PI_NOP 0000240 71 | #define PI_CLC 0000241 72 | #define PI_CLV 0000242 73 | #define PI_CLVC 0000243 74 | #define PI_CLZ 0000244 75 | #define PI_CLZC 0000245 76 | #define PI_CLZV 0000246 77 | #define PI_CLZVC 0000247 78 | #define PI_CLN 0000250 79 | #define PI_CLNC 0000251 80 | #define PI_CLNV 0000252 81 | #define PI_CLNVC 0000253 82 | #define PI_CLNZ 0000254 83 | #define PI_CLNZC 0000255 84 | #define PI_CLNZV 0000256 85 | #define PI_CCC 0000257 86 | #define PI_NOP260 0000260 87 | #define PI_SEC 0000261 88 | #define PI_SEV 0000262 89 | #define PI_SEVC 0000263 90 | #define PI_SEZ 0000264 91 | #define PI_SEZC 0000265 92 | #define PI_SEZV 0000266 93 | #define PI_SEZVC 0000267 94 | #define PI_SEN 0000270 95 | #define PI_SENC 0000271 96 | #define PI_SENV 0000272 97 | #define PI_SENVC 0000273 98 | #define PI_SENZ 0000274 99 | #define PI_SENZC 0000275 100 | #define PI_SENZV 0000276 101 | #define PI_SCC 0000277 102 | #define PI_MED 0076600 103 | 104 | // Commands -- single operand 105 | #define PI_RTS 0000200 106 | 107 | // Commands -- two operands 108 | #define PI_JMP 0000100 109 | #define PI_SWAB 0000300 110 | #define PI_CLR 0005000 111 | #define PI_COM 0005100 112 | #define PI_INC 0005200 113 | #define PI_DEC 0005300 114 | #define PI_NEG 0005400 115 | #define PI_ADC 0005500 116 | #define PI_SBC 0005600 117 | #define PI_TST 0005700 118 | #define PI_ROR 0006000 119 | #define PI_ROL 0006100 120 | #define PI_ASR 0006200 121 | #define PI_ASL 0006300 122 | #define PI_MARK 0006400 123 | #define PI_SXT 0006700 124 | #define PI_MTPS 0106400 125 | #define PI_MFPS 0106700 126 | 127 | // Commands -- branchs 128 | #define PI_BR 0000400 129 | #define PI_BNE 0001000 130 | #define PI_BEQ 0001400 131 | #define PI_BGE 0002000 132 | #define PI_BLT 0002400 133 | #define PI_BGT 0003000 134 | #define PI_BLE 0003400 135 | #define PI_BPL 0100000 136 | #define PI_BMI 0100400 137 | #define PI_BHI 0101000 138 | #define PI_BLOS 0101400 139 | #define PI_BVC 0102000 140 | #define PI_BVS 0102400 141 | #define PI_BHIS 0103000 142 | #define PI_BLO 0103400 143 | 144 | #define PI_EMT 0104000 145 | #define PI_TRAP 0104400 146 | 147 | // Commands -- three operands 148 | #define PI_JSR 0004000 149 | #define PI_MUL 0070000 150 | #define PI_DIV 0071000 151 | #define PI_ASH 0072000 152 | #define PI_ASHC 0073000 153 | #define PI_XOR 0074000 154 | #define PI_SOB 0077000 155 | 156 | // Commands -- four operands 157 | #define PI_MOV 0010000 158 | #define PI_CMP 0020000 159 | #define PI_BIT 0030000 160 | #define PI_BIC 0040000 161 | #define PI_BIS 0050000 162 | 163 | #define PI_ADD 0060000 164 | #define PI_SUB 0160000 165 | 166 | // Commands -- VM2 specifics 167 | #define PI_MUL 0070000 168 | #define PI_DIV 0071000 169 | #define PI_ASH 0072000 170 | #define PI_ASHC 0073000 171 | #define PI_FADD 0075000 172 | #define PI_FSUB 0075010 173 | #define PI_FMUL 0075020 174 | #define PI_FDIV 0075030 175 | 176 | // Commands -- special commands, HALT mode only 177 | #define PI_START 0000012 // Return to USER mode; PC := CPC; PSW := CPS 178 | #define PI_STEP 0000016 179 | #define PI_RSEL 0000020 // R0 := SEL - Read SEL register 180 | #define PI_MFUS 0000021 // R0 := (R5)+ 181 | #define PI_RCPC 0000022 // R0 := CPC 182 | #define PI_RCPS 0000024 // R0 := CPS 183 | #define PI_MTUS 0000031 // -(R5) := R0 184 | #define PI_WCPC 0000032 // CPC := R0 185 | #define PI_WCPS 0000034 // CPS := R0 186 | 187 | -------------------------------------------------------------------------------- /shell_minimal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 48 | 49 | 50 |
51 |
emscripten
52 |
Downloading...
53 |
54 | 55 |
56 |
57 | 58 |
59 |
60 |
61 | Resize canvas 62 | Lock/hide mouse pointer 63 |     64 | 66 |
67 | 68 |
69 | 70 |
71 | 149 | {{{ SCRIPT }}} 150 | 151 | 152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /emubase/Memory.h: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Memory.h Memory controller class 12 | 13 | #pragma once 14 | 15 | #include "Defines.h" 16 | #include "Board.h" 17 | 18 | class CProcessor; 19 | 20 | 21 | ////////////////////////////////////////////////////////////////////// 22 | 23 | // TranslateAddress result code 24 | #define ADDRTYPE_RAM0 0 ///< RAM plane 0 25 | #define ADDRTYPE_RAM1 1 ///< RAM plane 1 26 | #define ADDRTYPE_RAM2 2 ///< RAM plane 2 27 | #define ADDRTYPE_RAM12 4 ///< RAM plane 1 & 2 - a special case for CPU memory 28 | #define ADDRTYPE_ROM 32 ///< ROM 29 | #define ADDRTYPE_ROMCART1 40 ///< ADDRTYPE_ROM + 8 -- ROM cartridge #1 30 | #define ADDRTYPE_ROMCART2 48 ///< ADDRTYPE_ROM + 16 -- ROM cartridge #2 31 | #define ADDRTYPE_IO 64 ///< I/O port; bits 5..0 -- device number 32 | #define ADDRTYPE_NONE 128 ///< No data 33 | #define ADDRTYPE_DENY 255 ///< Access denied 34 | #define ADDRTYPE_MASK_RAM 7 ///< Mask to get memory plane number 35 | 36 | 37 | ////////////////////////////////////////////////////////////////////// 38 | 39 | 40 | /// \brief Memory control device for CPU or PPU (abstract class) 41 | class CMemoryController 42 | { 43 | protected: 44 | CMotherboard* m_pBoard; ///< Motherboard attached 45 | CProcessor* m_pProcessor; ///< Processor attached 46 | uint8_t* m_pMapping; ///< Memory mapping 47 | CBusDevice** m_pDevices; ///< Attached bus devices 48 | int m_nDeviceCount; 49 | public: 50 | CMemoryController(); 51 | virtual ~CMemoryController(); 52 | void Attach(CMotherboard* board, CProcessor* processor) 53 | { m_pBoard = board; m_pProcessor = processor; } 54 | /// \brief Attach/reattach bus devices 55 | void AttachDevices(const CBusDevice** pDevices); 56 | virtual void UpdateMemoryMap(); 57 | // Reset to initial state 58 | virtual void DCLO_Signal() = 0; ///< DCLO signal 59 | virtual void ResetDevices() = 0; ///< INIT signal 60 | public: // Access to memory 61 | /// \brief Read word 62 | uint16_t GetWord(uint16_t address, bool okHaltMode, bool okExec); 63 | /// \brief Write word 64 | void SetWord(uint16_t address, bool okHaltMode, uint16_t word); 65 | /// \brief Read byte 66 | uint8_t GetByte(uint16_t address, bool okHaltMode); 67 | /// \brief Write byte 68 | void SetByte(uint16_t address, bool okHaltMode, uint8_t byte); 69 | /// \brief Read word from memory for debugger 70 | uint16_t GetWordView(uint16_t address, bool okHaltMode, bool okExec, int* pAddrType) const; 71 | /// \brief Read word from port for debugger 72 | virtual uint16_t GetPortView(uint16_t address) const = 0; 73 | /// \brief Read SEL register 74 | virtual uint16_t GetSelRegister() = 0; 75 | public: // Saving/loading emulator status (64 bytes) 76 | virtual void SaveToImage(uint8_t* pImage) = 0; 77 | virtual void LoadFromImage(const uint8_t* pImage) = 0; 78 | protected: 79 | /// \brief Determite memory type for given address - see ADDRTYPE_Xxx constants 80 | /// \param okHaltMode processor mode (USER/HALT) 81 | /// \param okExec true: read instruction for execution; false: read memory 82 | /// \param pOffset result -- offset in memory plane 83 | virtual int TranslateAddress(uint16_t address, bool okHaltMode, bool okExec, uint16_t* pOffset, bool okView = false) const = 0; 84 | protected: // Access to I/O ports 85 | virtual uint16_t GetPortWord(uint16_t address) = 0; 86 | virtual void SetPortWord(uint16_t address, uint16_t word) = 0; 87 | virtual uint8_t GetPortByte(uint16_t address) = 0; 88 | virtual void SetPortByte(uint16_t address, uint8_t byte) = 0; 89 | }; 90 | 91 | /// \brief CPU memory control device 92 | class CFirstMemoryController : public CMemoryController 93 | { 94 | friend class CMotherboard; 95 | public: 96 | CFirstMemoryController(); 97 | virtual void DCLO_Signal(); ///< DCLO signal 98 | virtual void ResetDevices(); ///< INIT signal 99 | public: 100 | virtual int TranslateAddress(uint16_t address, bool okHaltMode, bool okExec, uint16_t* pOffset, bool okView) const; 101 | virtual uint16_t GetSelRegister() { return 0160000; } 102 | virtual uint16_t GetPortView(uint16_t address) const; 103 | protected: // Access to I/O ports 104 | virtual uint16_t GetPortWord(uint16_t address); 105 | virtual void SetPortWord(uint16_t address, uint16_t word); 106 | virtual uint8_t GetPortByte(uint16_t address); 107 | virtual void SetPortByte(uint16_t address, uint8_t byte); 108 | public: // Saving/loading emulator status (64 bytes) 109 | virtual void SaveToImage(uint8_t* pImage); 110 | virtual void LoadFromImage(const uint8_t* pImage); 111 | public: // CPU specific 112 | bool SerialInput(uint8_t inputByte); 113 | bool NetworkInput(uint8_t inputByte); 114 | protected: // Implementation 115 | uint16_t m_NetStation; ///< Network station number 116 | uint16_t m_Port176560; ///< Network receiver state 117 | uint16_t m_Port176562; ///< Network receiver data (bits 0-7) 118 | uint16_t m_Port176564; ///< Network translator state 119 | uint16_t m_Port176566; ///< Network translator data (bits 0-7) 120 | uint16_t m_Port176640; ///< Plane address register 121 | uint16_t m_Port176642; ///< Plane 1 & 2 data register 122 | uint16_t m_Port176644; 123 | uint16_t m_Port176646; 124 | uint16_t m_Port176570; ///< RS-232 receiver state 125 | uint16_t m_Port176572; ///< RS-232 receiver data (bits 0-7) 126 | uint16_t m_Port176574; ///< RS-232 translator state 127 | uint16_t m_Port176576; ///< RS-232 translator data (bits 0-7) 128 | }; 129 | 130 | /// \brief PPU memory control device 131 | class CSecondMemoryController : public CMemoryController 132 | { 133 | friend class CMotherboard; 134 | public: 135 | CSecondMemoryController(); 136 | virtual void UpdateMemoryMap(); 137 | virtual void DCLO_Signal(); ///< DCLO signal 138 | virtual void ResetDevices(); ///< INIT signal 139 | virtual void DCLO_177716(); 140 | virtual void Init_177716(); 141 | public: 142 | virtual int TranslateAddress(uint16_t address, bool okHaltMode, bool okExec, uint16_t* pOffset, bool okView) const; 143 | virtual uint16_t GetSelRegister() { return 0160000; } 144 | virtual uint16_t GetPortView(uint16_t address) const; 145 | protected: // Access to I/O ports 146 | virtual uint16_t GetPortWord(uint16_t address); 147 | virtual void SetPortWord(uint16_t address, uint16_t word); 148 | virtual uint8_t GetPortByte(uint16_t address); 149 | virtual void SetPortByte(uint16_t address, uint8_t byte); //TODO 150 | public: // Saving/loading emulator status (64 bytes) 151 | virtual void SaveToImage(uint8_t* pImage); 152 | virtual void LoadFromImage(const uint8_t* pImage); 153 | public: // PPU specifics 154 | void KeyboardEvent(uint8_t scancode, bool okPressed); ///< Keyboard key pressed or released 155 | bool TapeInput(bool inputBit); 156 | bool TapeOutput(); 157 | protected: // Implementation 158 | uint16_t m_Port177010; ///< Plane address register 159 | uint16_t m_Port177012; ///< Plane 0 data register 160 | uint16_t m_Port177014; ///< Plane 1 & 2 data register 161 | 162 | uint16_t m_Port177026; ///< Plane mask 163 | uint16_t m_Port177024; ///< SpriteByte 164 | uint16_t m_Port177020; ///< Background color 1 165 | uint16_t m_Port177022; ///< Background color 2 166 | uint16_t m_Port177016; ///< Pixel Color 167 | 168 | uint16_t m_Port177700; ///< Keyboard status 169 | uint16_t m_Port177702; ///< Keyboard data 170 | uint16_t m_Port177716; ///< System control register 171 | 172 | uint16_t m_Port177054; ///< address space control 173 | 174 | uint8_t m_Port177100; ///< i8255 port A -- Parallel port output data 175 | uint8_t m_Port177101; ///< i8255 port B 176 | uint8_t m_Port177102; ///< i8255 port C 177 | }; 178 | 179 | 180 | ////////////////////////////////////////////////////////////////////// 181 | -------------------------------------------------------------------------------- /emubase/Emubase.h: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Emubase.h Header for use of all emubase classes 12 | 13 | #pragma once 14 | 15 | #include "Board.h" 16 | #include "Processor.h" 17 | #include "Memory.h" 18 | 19 | 20 | ////////////////////////////////////////////////////////////////////// 21 | // Disassembler 22 | 23 | /// \brief Disassemble one instruction of KM1801VM2 processor 24 | /// \param[in] pMemory Memory image (we read only words of the instruction) 25 | /// \param[in] addr Address of the instruction 26 | /// \param[out] sInstr Instruction mnemonics buffer - at least 8 characters 27 | /// \param[out] sArg Instruction arguments buffer - at least 32 characters 28 | /// \return Number of words in the instruction 29 | uint16_t DisassembleInstruction(const uint16_t* pMemory, uint16_t addr, TCHAR* sInstr, TCHAR* sArg); 30 | 31 | 32 | ////////////////////////////////////////////////////////////////////// 33 | // CFloppy 34 | 35 | #define SAMPLERATE 22050 36 | 37 | #define FLOPPY_FSM_IDLE 0 38 | 39 | #define FLOPPY_CMD_CORRECTION250 04 40 | #define FLOPPY_CMD_CORRECTION500 010 41 | #define FLOPPY_CMD_ENGINESTART 020 ///< Engine on/off 42 | #define FLOPPY_CMD_SIDEUP 040 ///< Side: 1 -- upper head, 0 -- lower head 43 | #define FLOPPY_CMD_DIR 0100 ///< Direction: 0 -- to center (towards trk0), 1 -- from center (towards trk80) 44 | #define FLOPPY_CMD_STEP 0200 ///< Step / ready 45 | #define FLOPPY_CMD_SEARCHSYNC 0400 ///< Search sync 46 | #define FLOPPY_CMD_SKIPSYNC 01000 ///< Skip sync 47 | 48 | #define FLOPPY_STATUS_TRACK0 01 ///< Track 0 flag 49 | #define FLOPPY_STATUS_RDY 02 ///< Ready status 50 | #define FLOPPY_STATUS_WRITEPROTECT 04 ///< Write protect 51 | #define FLOPPY_STATUS_MOREDATA 0200 ///< Need more data flag 52 | #define FLOPPY_STATUS_CHECKSUMOK 040000 ///< Checksum verified OK 53 | #define FLOPPY_STATUS_INDEXMARK 0100000 ///< Index flag, indicates the beginning of track 54 | 55 | #define FLOPPY_RAWTRACKSIZE 6250 ///< Raw track size, bytes 56 | #define FLOPPY_RAWMARKERSIZE (FLOPPY_RAWTRACKSIZE / 2) 57 | #define FLOPPY_INDEXLENGTH 150 ///< Length of index hole, in bytes of raw track image 58 | 59 | /// \brief Floppy drive (one of four drives in the floppy controller) 60 | /// \sa CFloppyController 61 | struct CFloppyDrive 62 | { 63 | FILE* fpFile; ///< File pointer of the disk image file 64 | bool okNetRT11Image; ///< true - .rtd image, false - .dsk image 65 | bool okReadOnly; ///< Write protection flag 66 | uint16_t track; ///< Track number: from 0 to 79 67 | uint16_t side; ///< Disk side: 0 or 1 68 | uint16_t dataptr; ///< Data offset within m_data - "head" position 69 | uint16_t datatrack; ///< Track number of data in m_data array 70 | uint16_t dataside; ///< Disk side of data in m_data array 71 | uint8_t data[FLOPPY_RAWTRACKSIZE]; ///< Raw track image for the current track 72 | uint8_t marker[FLOPPY_RAWMARKERSIZE]; ///< Marker positions 73 | 74 | public: 75 | CFloppyDrive(); 76 | void Reset(); ///< Reset the device 77 | }; 78 | 79 | /// \brief UKNC floppy controller (MZ standard) 80 | /// \sa CFloppyDrive 81 | class CFloppyController 82 | { 83 | protected: 84 | CFloppyDrive m_drivedata[4]; ///< Floppy drives 85 | CFloppyDrive* m_pDrive; ///< Current drive 86 | uint16_t m_drive; ///< Current drive number: from 0 to 3 87 | uint16_t m_track; ///< Track number: from 0 to 79 88 | uint16_t m_side; ///< Disk side: 0 or 1 89 | uint16_t m_status; ///< See FLOPPY_STATUS_XXX defines 90 | uint16_t m_flags; ///< See FLOPPY_CMD_XXX defines 91 | uint16_t m_datareg; ///< Read mode data register 92 | uint16_t m_writereg; ///< Write mode data register 93 | bool m_writeflag; ///< Write mode data register has data 94 | bool m_writemarker; ///< Write marker in m_marker 95 | uint16_t m_shiftreg; ///< Write mode shift register 96 | bool m_shiftflag; ///< Write mode shift register has data 97 | bool m_shiftmarker; ///< Write marker in m_marker 98 | bool m_writing; ///< true = write mode, false = read mode 99 | bool m_searchsync; ///< Read sub-mode: true = search for sync, false = just read 100 | bool m_crccalculus; ///< true = CRC is calculated now 101 | bool m_trackchanged; ///< true = m_data was changed - need to save it into the file 102 | bool m_okTrace; ///< Trace mode on/off 103 | 104 | public: 105 | CFloppyController(); 106 | ~CFloppyController(); 107 | void Reset(); ///< Reset the device 108 | 109 | public: 110 | /// \brief Attach the image to the drive -- insert disk 111 | bool AttachImage(int drive, LPCTSTR sFileName); 112 | /// \brief Detach image from the drive -- remove disk 113 | void DetachImage(int drive); 114 | /// \brief Check if the drive has an image attached 115 | bool IsAttached(int drive) const { return (m_drivedata[drive].fpFile != nullptr); } 116 | /// \brief Check if the drive's attached image is read-only 117 | bool IsReadOnly(int drive) const { return m_drivedata[drive].okReadOnly; } 118 | /// \brief Check if floppy engine now rotates 119 | bool IsEngineOn() const { return (m_flags & FLOPPY_CMD_ENGINESTART) != 0; } 120 | uint16_t GetData(void); ///< Reading port 177132 -- data 121 | uint16_t GetState(void); ///< Reading port 177130 -- device status 122 | void SetCommand(uint16_t cmd); ///< Writing to port 177130 -- commands 123 | void WriteData(uint16_t data); ///< Writing to port 177132 -- data 124 | void Periodic(); ///< Rotate disk; call it each 64 us -- 15625 times per second 125 | void SetTrace(bool okTrace) { m_okTrace = okTrace; } // Set trace mode on/off 126 | 127 | private: 128 | void PrepareTrack(); 129 | void FlushChanges(); ///< If current track was changed - save it 130 | }; 131 | 132 | 133 | ////////////////////////////////////////////////////////////////////// 134 | // CHardDrive 135 | 136 | #define IDE_DISK_SECTOR_SIZE 512 137 | 138 | /// \brief UKNC IDE hard drive 139 | class CHardDrive 140 | { 141 | protected: 142 | FILE* m_fpFile; ///< File pointer for the attached HDD image 143 | bool m_okReadOnly; ///< Flag indicating that the HDD image file is read-only 144 | bool m_okInverted; ///< Flag indicating that the HDD image has inverted bits 145 | uint8_t m_status; ///< IDE status register, see IDE_STATUS_XXX constants 146 | uint8_t m_error; ///< IDE error register, see IDE_ERROR_XXX constants 147 | uint8_t m_command; ///< Current IDE command, see IDE_COMMAND_XXX constants 148 | int m_numcylinders; ///< Cylinder count 149 | int m_numheads; ///< Head count 150 | int m_numsectors; ///< Sectors per track 151 | int m_curcylinder; ///< Current cylinder number 152 | int m_curhead; ///< Current head number 153 | int m_cursector; ///< Current sector number 154 | int m_curheadreg; ///< Current head number 155 | int m_sectorcount; ///< Sector counter for read/write operations 156 | uint8_t m_buffer[IDE_DISK_SECTOR_SIZE]; ///< Sector data buffer 157 | int m_bufferoffset; ///< Current offset within sector: 0..511 158 | int m_timeoutcount; ///< Timeout counter to wait for the next event 159 | int m_timeoutevent; ///< Current stage of operation, see TimeoutEvent enum 160 | 161 | public: 162 | CHardDrive(); 163 | ~CHardDrive(); 164 | /// \brief Reset the device. 165 | void Reset(); 166 | /// \brief Attach HDD image file to the device 167 | bool AttachImage(LPCTSTR sFileName); 168 | /// \brief Detach HDD image file from the device 169 | void DetachImage(); 170 | /// \brief Check if the attached hard drive image is read-only 171 | bool IsReadOnly() const { return m_okReadOnly; } 172 | 173 | public: 174 | /// \brief Read word from the device port 175 | uint16_t ReadPort(uint16_t port); 176 | /// \brief Write word th the device port 177 | void WritePort(uint16_t port, uint16_t data); 178 | /// \brief Rotate disk 179 | void Periodic(); 180 | 181 | private: 182 | uint32_t CalculateOffset() const; ///< Calculate sector offset in the HDD image 183 | void HandleCommand(uint8_t command); ///< Handle the IDE command 184 | void ReadNextSector(); 185 | void ReadSectorDone(); 186 | void WriteSectorDone(); 187 | void NextSector(); ///< Advance to the next sector, CHS-based 188 | void ContinueRead(); 189 | void ContinueWrite(); 190 | void IdentifyDrive(); ///< Prepare m_buffer for the IDENTIFY DRIVE command 191 | }; 192 | 193 | 194 | ////////////////////////////////////////////////////////////////////// 195 | -------------------------------------------------------------------------------- /miniz/zip.h: -------------------------------------------------------------------------------- 1 | /* 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 3 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 4 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 5 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 6 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 7 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 8 | * OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | #pragma once 12 | #ifndef ZIP_H 13 | #define ZIP_H 14 | 15 | #include 16 | #include 17 | 18 | #ifndef ZIP_SHARED 19 | # define ZIP_EXPORT 20 | #else 21 | # ifdef _WIN32 22 | # ifdef ZIP_BUILD_SHARED 23 | # define ZIP_EXPORT __declspec(dllexport) 24 | # else 25 | # define ZIP_EXPORT __declspec(dllimport) 26 | # endif 27 | # else 28 | # define ZIP_EXPORT __attribute__ ((visibility ("default"))) 29 | # endif 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) 37 | // 64-bit Windows is the only mainstream platform 38 | // where sizeof(long) != sizeof(void*) 39 | #ifdef _WIN64 40 | typedef long long ssize_t; /* byte count or error */ 41 | #else 42 | typedef long ssize_t; /* byte count or error */ 43 | #endif 44 | #endif 45 | 46 | #ifndef MAX_PATH 47 | #define MAX_PATH 32767 /* # chars in a path name including NULL */ 48 | #endif 49 | 50 | /** 51 | * @mainpage 52 | * 53 | * Documenation for @ref zip. 54 | */ 55 | 56 | /** 57 | * @addtogroup zip 58 | * @{ 59 | */ 60 | 61 | /** 62 | * Default zip compression level. 63 | */ 64 | #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 65 | 66 | /** 67 | * Error codes 68 | */ 69 | #define ZIP_ENOINIT -1 // not initialized 70 | #define ZIP_EINVENTNAME -2 // invalid entry name 71 | #define ZIP_ENOENT -3 // entry not found 72 | #define ZIP_EINVMODE -4 // invalid zip mode 73 | #define ZIP_EINVLVL -5 // invalid compression level 74 | #define ZIP_ENOSUP64 -6 // no zip 64 support 75 | #define ZIP_EMEMSET -7 // memset error 76 | #define ZIP_EWRTENT -8 // cannot write data to entry 77 | #define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor 78 | #define ZIP_EINVIDX -10 // invalid index 79 | #define ZIP_ENOHDR -11 // header not found 80 | #define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer 81 | #define ZIP_ECRTHDR -13 // cannot create entry header 82 | #define ZIP_EWRTHDR -14 // cannot write entry header 83 | #define ZIP_EWRTDIR -15 // cannot write to central dir 84 | #define ZIP_EOPNFILE -16 // cannot open file 85 | #define ZIP_EINVENTTYPE -17 // invalid entry type 86 | #define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation 87 | #define ZIP_ENOFILE -19 // file not found 88 | #define ZIP_ENOPERM -20 // no permission 89 | #define ZIP_EOOMEM -21 // out of memory 90 | #define ZIP_EINVZIPNAME -22 // invalid zip archive name 91 | #define ZIP_EMKDIR -23 // make dir error 92 | #define ZIP_ESYMLINK -24 // symlink error 93 | #define ZIP_ECLSZIP -25 // close archive error 94 | #define ZIP_ECAPSIZE -26 // capacity size too small 95 | #define ZIP_EFSEEK -27 // fseek error 96 | #define ZIP_EFREAD -28 // fread error 97 | #define ZIP_EFWRITE -29 // fwrite error 98 | 99 | /** 100 | * Looks up the error message string coresponding to an error number. 101 | * @param errnum error number 102 | * @return error message string coresponding to errnum or NULL if error is not 103 | * found. 104 | */ 105 | extern ZIP_EXPORT const char *zip_strerror(int errnum); 106 | 107 | /** 108 | * @struct zip_t 109 | * 110 | * This data structure is used throughout the library to represent zip archive - 111 | * forward declaration. 112 | */ 113 | struct zip_t; 114 | 115 | /** 116 | * Opens zip archive with compression level using the given mode. 117 | * 118 | * @param zipname zip archive file name. 119 | * @param level compression level (0-9 are the standard zlib-style levels). 120 | * @param mode file access mode. 121 | * - 'r': opens a file for reading/extracting (the file must exists). 122 | * - 'w': creates an empty file for writing. 123 | * - 'a': appends to an existing archive. 124 | * 125 | * @return the zip archive handler or NULL on error 126 | */ 127 | extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level, 128 | char mode); 129 | 130 | /** 131 | * Closes the zip archive, releases resources - always finalize. 132 | * 133 | * @param zip zip archive handler. 134 | */ 135 | extern ZIP_EXPORT void zip_close(struct zip_t *zip); 136 | 137 | /** 138 | * Determines if the archive has a zip64 end of central directory headers. 139 | * 140 | * @param zip zip archive handler. 141 | * 142 | * @return the return code - 1 (true), 0 (false), negative number (< 0) on 143 | * error. 144 | */ 145 | extern ZIP_EXPORT int zip_is64(struct zip_t *zip); 146 | 147 | /** 148 | * Opens an entry by name in the zip archive. 149 | * 150 | * For zip archive opened in 'w' or 'a' mode the function will append 151 | * a new entry. In readonly mode the function tries to locate the entry 152 | * in global dictionary. 153 | * 154 | * @param zip zip archive handler. 155 | * @param entryname an entry name in local dictionary. 156 | * 157 | * @return the return code - 0 on success, negative number (< 0) on error. 158 | */ 159 | extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname); 160 | 161 | /** 162 | * Opens a new entry by index in the zip archive. 163 | * 164 | * This function is only valid if zip archive was opened in 'r' (readonly) mode. 165 | * 166 | * @param zip zip archive handler. 167 | * @param index index in local dictionary. 168 | * 169 | * @return the return code - 0 on success, negative number (< 0) on error. 170 | */ 171 | extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, int index); 172 | 173 | /** 174 | * Closes a zip entry, flushes buffer and releases resources. 175 | * 176 | * @param zip zip archive handler. 177 | * 178 | * @return the return code - 0 on success, negative number (< 0) on error. 179 | */ 180 | extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip); 181 | 182 | /** 183 | * Returns a local name of the current zip entry. 184 | * 185 | * The main difference between user's entry name and local entry name 186 | * is optional relative path. 187 | * Following .ZIP File Format Specification - the path stored MUST not contain 188 | * a drive or device letter, or a leading slash. 189 | * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' 190 | * for compatibility with Amiga and UNIX file systems etc. 191 | * 192 | * @param zip: zip archive handler. 193 | * 194 | * @return the pointer to the current zip entry name, or NULL on error. 195 | */ 196 | extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip); 197 | 198 | /** 199 | * Returns an index of the current zip entry. 200 | * 201 | * @param zip zip archive handler. 202 | * 203 | * @return the index on success, negative number (< 0) on error. 204 | */ 205 | extern ZIP_EXPORT int zip_entry_index(struct zip_t *zip); 206 | 207 | /** 208 | * Determines if the current zip entry is a directory entry. 209 | * 210 | * @param zip zip archive handler. 211 | * 212 | * @return the return code - 1 (true), 0 (false), negative number (< 0) on 213 | * error. 214 | */ 215 | extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip); 216 | 217 | /** 218 | * Returns an uncompressed size of the current zip entry. 219 | * 220 | * @param zip zip archive handler. 221 | * 222 | * @return the uncompressed size in bytes. 223 | */ 224 | extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip); 225 | 226 | /** 227 | * Returns CRC-32 checksum of the current zip entry. 228 | * 229 | * @param zip zip archive handler. 230 | * 231 | * @return the CRC-32 checksum. 232 | */ 233 | extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip); 234 | 235 | /** 236 | * Compresses an input buffer for the current zip entry. 237 | * 238 | * @param zip zip archive handler. 239 | * @param buf input buffer. 240 | * @param bufsize input buffer size (in bytes). 241 | * 242 | * @return the return code - 0 on success, negative number (< 0) on error. 243 | */ 244 | extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf, 245 | size_t bufsize); 246 | 247 | /** 248 | * Compresses a file for the current zip entry. 249 | * 250 | * @param zip zip archive handler. 251 | * @param filename input file. 252 | * 253 | * @return the return code - 0 on success, negative number (< 0) on error. 254 | */ 255 | extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename); 256 | 257 | /** 258 | * Extracts the current zip entry into output buffer. 259 | * 260 | * The function allocates sufficient memory for a output buffer. 261 | * 262 | * @param zip zip archive handler. 263 | * @param buf output buffer. 264 | * @param bufsize output buffer size (in bytes). 265 | * 266 | * @note remember to release memory allocated for a output buffer. 267 | * for large entries, please take a look at zip_entry_extract function. 268 | * 269 | * @return the return code - the number of bytes actually read on success. 270 | * Otherwise a -1 on error. 271 | */ 272 | extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf, 273 | size_t *bufsize); 274 | 275 | /** 276 | * Extracts the current zip entry into a memory buffer using no memory 277 | * allocation. 278 | * 279 | * @param zip zip archive handler. 280 | * @param buf preallocated output buffer. 281 | * @param bufsize output buffer size (in bytes). 282 | * 283 | * @note ensure supplied output buffer is large enough. 284 | * zip_entry_size function (returns uncompressed size for the current 285 | * entry) can be handy to estimate how big buffer is needed. 286 | * For large entries, please take a look at zip_entry_extract function. 287 | * 288 | * @return the return code - the number of bytes actually read on success. 289 | * Otherwise a -1 on error (e.g. bufsize is not large enough). 290 | */ 291 | extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, 292 | size_t bufsize); 293 | 294 | /** 295 | * Extracts the current zip entry into output file. 296 | * 297 | * @param zip zip archive handler. 298 | * @param filename output file. 299 | * 300 | * @return the return code - 0 on success, negative number (< 0) on error. 301 | */ 302 | extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename); 303 | 304 | /** 305 | * Extracts the current zip entry using a callback function (on_extract). 306 | * 307 | * @param zip zip archive handler. 308 | * @param on_extract callback function. 309 | * @param arg opaque pointer (optional argument, which you can pass to the 310 | * on_extract callback) 311 | * 312 | * @return the return code - 0 on success, negative number (< 0) on error. 313 | */ 314 | extern ZIP_EXPORT int 315 | zip_entry_extract(struct zip_t *zip, 316 | size_t (*on_extract)(void *arg, unsigned long long offset, 317 | const void *data, size_t size), 318 | void *arg); 319 | 320 | /** 321 | * Returns the number of all entries (files and directories) in the zip archive. 322 | * 323 | * @param zip zip archive handler. 324 | * 325 | * @return the return code - the number of entries on success, negative number 326 | * (< 0) on error. 327 | */ 328 | extern ZIP_EXPORT int zip_entries_total(struct zip_t *zip); 329 | 330 | /** 331 | * Deletes zip archive entries. 332 | * 333 | * @param zip zip archive handler. 334 | * @param entries array of zip archive entries to be deleted. 335 | * @param len the number of entries to be deleted. 336 | * @return the number of deleted entries, or negative number (< 0) on error. 337 | */ 338 | extern ZIP_EXPORT int zip_entries_delete(struct zip_t *zip, 339 | char *const entries[], size_t len); 340 | 341 | /** 342 | * Extracts a zip archive stream into directory. 343 | * 344 | * If on_extract is not NULL, the callback will be called after 345 | * successfully extracted each zip entry. 346 | * Returning a negative value from the callback will cause abort and return an 347 | * error. The last argument (void *arg) is optional, which you can use to pass 348 | * data to the on_extract callback. 349 | * 350 | * @param stream zip archive stream. 351 | * @param size stream size. 352 | * @param dir output directory. 353 | * @param on_extract on extract callback. 354 | * @param arg opaque pointer. 355 | * 356 | * @return the return code - 0 on success, negative number (< 0) on error. 357 | */ 358 | extern ZIP_EXPORT int 359 | zip_stream_extract(const char *stream, size_t size, const char *dir, 360 | int (*on_extract)(const char *filename, void *arg), 361 | void *arg); 362 | 363 | /** 364 | * Opens zip archive stream into memory. 365 | * 366 | * @param stream zip archive stream. 367 | * @param size stream size. 368 | * 369 | * @return the zip archive handler or NULL on error 370 | */ 371 | extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size, 372 | int level, char mode); 373 | 374 | /** 375 | * Copy zip archive stream output buffer. 376 | * 377 | * @param zip zip archive handler. 378 | * @param buf output buffer. User should free buf. 379 | * @param bufsize output buffer size (in bytes). 380 | * 381 | * @return copy size 382 | */ 383 | extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf, 384 | ssize_t *bufsize); 385 | 386 | /** 387 | * Close zip archive releases resources. 388 | * 389 | * @param zip zip archive handler. 390 | * 391 | * @return 392 | */ 393 | extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip); 394 | 395 | /** 396 | * Creates a new archive and puts files into a single zip archive. 397 | * 398 | * @param zipname zip archive file. 399 | * @param filenames input files. 400 | * @param len: number of input files. 401 | * 402 | * @return the return code - 0 on success, negative number (< 0) on error. 403 | */ 404 | extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[], 405 | size_t len); 406 | 407 | /** 408 | * Extracts a zip archive file into directory. 409 | * 410 | * If on_extract_entry is not NULL, the callback will be called after 411 | * successfully extracted each zip entry. 412 | * Returning a negative value from the callback will cause abort and return an 413 | * error. The last argument (void *arg) is optional, which you can use to pass 414 | * data to the on_extract_entry callback. 415 | * 416 | * @param zipname zip archive file. 417 | * @param dir output directory. 418 | * @param on_extract_entry on extract callback. 419 | * @param arg opaque pointer. 420 | * 421 | * @return the return code - 0 on success, negative number (< 0) on error. 422 | */ 423 | extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir, 424 | int (*on_extract_entry)(const char *filename, 425 | void *arg), 426 | void *arg); 427 | 428 | /** @} */ 429 | #ifdef __cplusplus 430 | } 431 | #endif 432 | 433 | #endif 434 | -------------------------------------------------------------------------------- /emubase/Board.h: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Board.h Motherboard class 12 | 13 | #pragma once 14 | 15 | #include "Defines.h" 16 | 17 | class CProcessor; 18 | class CMemoryController; 19 | 20 | // Floppy debug constants 21 | #define FLOPPY_FSM_WAITFORLSB 0 22 | #define FLOPPY_FSM_WAITFORMSB 1 23 | #define FLOPPY_FSM_WAITFORTERM1 2 24 | #define FLOPPY_FSM_WAITFORTERM2 3 25 | 26 | // Trace flags 27 | #define TRACE_NONE 0 // Turn off all tracing 28 | #define TRACE_FLOPPY 0100 // Trace floppies 29 | #define TRACE_PPU 0200 // Trace PPU instructions 30 | #define TRACE_CPU 0400 // Trace CPU instructions 31 | #define TRACE_ALL 0177777 // Trace all 32 | 33 | // Emulator image constants 34 | #define UKNCIMAGE_HEADER_SIZE 512 35 | #define UKNCIMAGE_SIZE (UKNCIMAGE_HEADER_SIZE + (32 + 64 * 3) * 1024) 36 | #define UKNCIMAGE_HEADER1 0x434E4B55 // "UKNC" 37 | #define UKNCIMAGE_HEADER2 0x214C5442 // "BTL!" 38 | #define UKNCIMAGE_VERSION 0x00010001 // 1.1 39 | 40 | #define KEYB_RUS 0x01 41 | #define KEYB_LAT 0x02 42 | #define KEYB_LOWERREG 0x10 43 | 44 | typedef struct chan_tag 45 | { 46 | uint8_t data; 47 | uint8_t ready; 48 | uint8_t irq; 49 | uint8_t rdwr; 50 | } chan_stc; 51 | 52 | typedef struct kbd_row_tag 53 | { 54 | uint8_t processed; 55 | uint8_t row_Y; 56 | } kbd_row; 57 | 58 | // Tape emulator callback used to read a tape recorded data. 59 | // Input: 60 | // samples Number of samples to play. 61 | // Output: 62 | // result Bit to put in tape input port. 63 | typedef bool (CALLBACK* TAPEREADCALLBACK)(unsigned int samples); 64 | 65 | // Tape emulator callback used to write a data to tape. 66 | // Input: 67 | // value Sample value to write. 68 | typedef void (CALLBACK* TAPEWRITECALLBACK)(int value, unsigned int samples); 69 | 70 | // Sound generator callback function type 71 | typedef void (CALLBACK* SOUNDGENCALLBACK)(unsigned short L, unsigned short R); 72 | 73 | // Network port callback for receiving 74 | // Output: 75 | // pbyte Byte received 76 | // result true means we have a new byte, false means not ready yet 77 | typedef bool (CALLBACK* NETWORKINCALLBACK)(uint8_t* pbyte); 78 | 79 | // Network port callback for translating 80 | // Input: 81 | // byte A byte to translate 82 | // Output: 83 | // result true means we translated the byte successfully, false means we have an error 84 | typedef bool (CALLBACK* NETWORKOUTCALLBACK)(uint8_t byte); 85 | 86 | // Serial port callback for receiving 87 | // Output: 88 | // pbyte Byte received 89 | // result true means we have a new byte, false means not ready yet 90 | typedef bool (CALLBACK* SERIALINCALLBACK)(uint8_t* pbyte); 91 | 92 | // Serial port callback for translating 93 | // Input: 94 | // byte A byte to translate 95 | // Output: 96 | // result true means we translated the byte successfully, false means we have an error 97 | typedef bool (CALLBACK* SERIALOUTCALLBACK)(uint8_t byte); 98 | 99 | // Parallel port output callback 100 | // Input: 101 | // byte An output byte 102 | // Output: 103 | // result true means OK, false means we have an error 104 | typedef bool (CALLBACK* PARALLELOUTCALLBACK)(uint8_t byte); 105 | 106 | // Terminal out callback -- CPU sends character by channel 0 107 | typedef void (CALLBACK* TERMINALOUTCALLBACK)(uint8_t byte); 108 | 109 | class CFloppyController; 110 | class CHardDrive; 111 | 112 | ////////////////////////////////////////////////////////////////////// 113 | 114 | 115 | /// \brief Bus device 116 | class CBusDevice 117 | { 118 | public: 119 | /// \brief Name of the device 120 | virtual LPCTSTR GetName() const = 0; 121 | /// \brief Device address ranges: [address, length] pairs, last pair is [0,0] 122 | virtual const uint16_t* GetAddressRanges() const = 0; 123 | 124 | virtual ~CBusDevice() { } 125 | }; 126 | 127 | /// \brief UKNC computer 128 | class CMotherboard 129 | { 130 | public: // Construct / destruct 131 | CMotherboard(); 132 | ~CMotherboard(); 133 | 134 | protected: // Devices 135 | CProcessor* m_pCPU; ///< CPU device 136 | CProcessor* m_pPPU; ///< PPU device 137 | CMemoryController* m_pFirstMemCtl; ///< CPU memory control 138 | CMemoryController* m_pSecondMemCtl; ///< PPU memory control 139 | CFloppyController* m_pFloppyCtl; ///< FDD control 140 | CHardDrive* m_pHardDrives[2]; ///< HDD control 141 | 142 | public: // Getting devices 143 | CProcessor* GetCPU() { return m_pCPU; } ///< Getter for m_pCPU 144 | CProcessor* GetPPU() { return m_pPPU; } ///< Getter for m_pPPU 145 | CMemoryController* GetCPUMemoryController() { return m_pFirstMemCtl; } ///< Get CPU memory controller 146 | CMemoryController* GetPPUMemoryController() { return m_pSecondMemCtl; } ///< Get PPU memory controller 147 | 148 | protected: 149 | CBusDevice** m_pCpuDevices; ///< List of CPU bus devices 150 | CBusDevice** m_pPpuDevices; ///< List of PPU bus devices 151 | public: 152 | const CBusDevice** GetCPUBusDevices() { return (const CBusDevice**) m_pCpuDevices; } 153 | const CBusDevice** GetPPUBusDevices() { return (const CBusDevice**) m_pPpuDevices; } 154 | 155 | protected: // Memory 156 | uint8_t* m_pRAM[3]; ///< RAM, three planes, 64 KB each 157 | uint8_t* m_pROM; ///< System ROM, 32 KB 158 | uint8_t* m_pROMCart[2]; ///< ROM cartridges #1 and #2, 24 KB each 159 | public: // Memory access 160 | uint16_t GetRAMWord(int plan, uint16_t offset) const; 161 | uint8_t GetRAMByte(int plan, uint16_t offset) const; 162 | void SetRAMWord(int plan, uint16_t offset, uint16_t word); 163 | void SetRAMByte(int plan, uint16_t offset, uint8_t byte); 164 | uint16_t GetROMWord(uint16_t offset) const; 165 | uint8_t GetROMByte(uint16_t offset) const; 166 | uint16_t GetROMCartWord(int cartno, uint16_t offset) const; 167 | uint8_t GetROMCartByte(int cartno, uint16_t offset) const; 168 | public: // Debug 169 | void DebugTicks(); ///< One Debug PPU tick -- use for debug step or debug breakpoint 170 | void SetCPUBreakpoints(const uint16_t* bps) { m_CPUbps = bps; } ///< Set CPU breakpoint list 171 | void SetPPUBreakpoints(const uint16_t* bps) { m_PPUbps = bps; } ///< Set PPU breakpoint list 172 | uint32_t GetTrace() const { return m_dwTrace; } 173 | void SetTrace(uint32_t dwTrace); 174 | chan_stc GetChannelStruct(unsigned char cpu, unsigned char chan, unsigned char tx) 175 | { 176 | //cpu==1 ,ppu==0; tx==1, rx==0 177 | if (cpu) 178 | { 179 | if (tx) 180 | return m_chancputx[chan]; 181 | else 182 | return m_chancpurx[chan]; 183 | } 184 | else 185 | { 186 | if (tx) 187 | return m_chanpputx[chan]; 188 | else 189 | return m_chanppurx[chan]; 190 | } 191 | } 192 | 193 | public: // System control 194 | void Reset(); ///< Reset computer 195 | void LoadROM(const uint8_t* pBuffer); ///< Load 32 KB ROM image from the biffer 196 | void LoadROMCartridge(int cartno, const uint8_t* pBuffer); ///< Load 24 KB ROM cartridge image 197 | void LoadRAM(int plan, const uint8_t* pBuffer); ///< Load 32 KB RAM image from the biffer 198 | void SetNetStation(uint16_t station); // Network station number 199 | void Tick8000(); ///< Tick 8.00 MHz 200 | void Tick6250(); ///< Tick 6.25 MHz 201 | void Tick50(); ///< Tick 50 Hz - goes to CPU/PPU EVNT line 202 | void TimerTick(); ///< Timer Tick, 2uS -- dividers are within timer routine 203 | void ResetFloppy(); ///< INIT signal for FDD 204 | uint16_t GetTimerValue(); ///< Returns current timer value 205 | uint16_t GetTimerValueView() { return m_timer; } ///< Returns current timer value for debugger 206 | uint16_t GetTimerReload(); ///< Returns timer reload value 207 | uint16_t GetTimerReloadView() { return m_timerreload; } ///< Returns timer reload value for debugger 208 | uint16_t GetTimerState(); ///< Returns timer state 209 | uint16_t GetTimerStateView() { return m_timerflags; } ///< Returns timer state for debugger 210 | 211 | void ChanWriteByCPU(uint8_t chan, uint8_t data); 212 | void ChanWriteByPPU(uint8_t chan, uint8_t data); 213 | uint8_t ChanReadByCPU(uint8_t chan); 214 | uint8_t ChanReadByPPU(uint8_t chan); 215 | 216 | uint8_t ChanRxStateGetCPU(uint8_t chan); 217 | uint8_t ChanTxStateGetCPU(uint8_t chan); 218 | uint8_t ChanRxStateGetPPU(); 219 | uint8_t ChanTxStateGetPPU(); 220 | void ChanRxStateSetCPU(uint8_t chan, uint8_t state); 221 | void ChanTxStateSetCPU(uint8_t chan, uint8_t state); 222 | void ChanRxStateSetPPU(uint8_t state); 223 | void ChanTxStateSetPPU(uint8_t state); 224 | 225 | void ChanResetByCPU(); 226 | void ChanResetByPPU(); 227 | 228 | void SetTimerReload(uint16_t val); ///< Sets timer reload value 229 | void SetTimerState(uint16_t val); ///< Sets timer state 230 | void ExecuteCPU(); ///< Execute one CPU instruction 231 | void ExecutePPU(); ///< Execute one PPU instruction 232 | bool SystemFrame(); ///< Do one frame -- use for normal run 233 | void KeyboardEvent(uint8_t scancode, bool okPressed); ///< Key pressed or released 234 | uint16_t GetKeyboardRegister(); 235 | uint16_t GetScannedKey() { return m_scanned_key; } 236 | int GetSoundChanges() const { return m_SoundChanges; } ///< Sound signal 0 to 1 changes since the beginning of the frame 237 | 238 | /// \brief Attach floppy image to the slot -- insert the disk. 239 | bool AttachFloppyImage(int slot, LPCTSTR sFileName); 240 | /// \brief Empty the floppy slot -- remove the disk. 241 | void DetachFloppyImage(int slot); 242 | /// \brief Check if the floppy attached. 243 | bool IsFloppyImageAttached(int slot) const; 244 | /// \brief Check if the attached floppy image is read-only. 245 | bool IsFloppyReadOnly(int slot) const; 246 | /// \brief Check if the floppy drive engine rotates the disks. 247 | bool IsFloppyEngineOn() const; 248 | uint16_t GetFloppyState(); 249 | uint16_t GetFloppyData(); 250 | void SetFloppyState(uint16_t val); 251 | void SetFloppyData(uint16_t val); 252 | 253 | /// \brief Check if ROM cartridge image assigned to the cartridge slot. 254 | bool IsROMCartridgeLoaded(int cartno) const; 255 | /// \brief Empty the ROM cartridge slot. 256 | void UnloadROMCartridge(int cartno); 257 | 258 | /// \brief Attach hard drive image to the slot. 259 | bool AttachHardImage(int slot, LPCTSTR sFileName); 260 | /// \brief Empty hard drive slot. 261 | void DetachHardImage(int slot); 262 | /// \brief Check if the hard drive attached. 263 | bool IsHardImageAttached(int slot) const; 264 | /// \brief Check if the attached hard drive image is read-only. 265 | bool IsHardImageReadOnly(int slot) const; 266 | uint16_t GetHardPortWord(int slot, uint16_t port); ///< To use from CSecondMemoryController only 267 | void SetHardPortWord(int slot, uint16_t port, uint16_t data); ///< To use from CSecondMemoryController only 268 | 269 | /// \brief Assign tape read callback function. 270 | void SetTapeReadCallback(TAPEREADCALLBACK callback, int sampleRate); 271 | /// \brief Assign write read callback function. 272 | void SetTapeWriteCallback(TAPEWRITECALLBACK callback, int sampleRate); 273 | /// \brief Assign sound output callback function. 274 | void SetSoundGenCallback(SOUNDGENCALLBACK callback); 275 | /// \brief Assign serial port input/output callback functions. 276 | void SetSerialCallbacks(SERIALINCALLBACK incallback, SERIALOUTCALLBACK outcallback); 277 | /// \brief Assign parallel port output callback function. 278 | void SetParallelOutCallback(PARALLELOUTCALLBACK outcallback); 279 | /// \brief Assign network port input/output callback functions. 280 | void SetNetworkCallbacks(NETWORKINCALLBACK incallback, NETWORKOUTCALLBACK outcallback); 281 | /// \brief Assign terminal callback functions. 282 | void SetTerminalCallback(TERMINALOUTCALLBACK callback) { m_TerminalOutCallback = callback; } 283 | public: // Saving/loading emulator status 284 | void SaveToImage(uint8_t* pImage); 285 | void LoadFromImage(const uint8_t* pImage); 286 | void SetSound(uint16_t val); 287 | private: // Timing 288 | uint16_t m_multiply; 289 | uint16_t freq_per[6]; 290 | uint16_t freq_out[6]; 291 | uint16_t freq_enable[6]; 292 | int m_pputicks; 293 | int m_cputicks; 294 | unsigned int m_lineticks; 295 | private: 296 | const uint16_t* m_CPUbps; ///< CPU breakpoint list, ends with 177777 value 297 | const uint16_t* m_PPUbps; ///< PPU breakpoint list, ends with 177777 value 298 | uint32_t m_dwTrace; ///< Trace flags 299 | 300 | uint16_t m_timer; 301 | uint16_t m_timerreload; 302 | uint16_t m_timerflags; 303 | uint16_t m_timerdivider; 304 | 305 | chan_stc m_chancputx[3]; 306 | chan_stc m_chancpurx[2]; 307 | chan_stc m_chanpputx[2]; 308 | chan_stc m_chanppurx[3]; 309 | uint8_t m_chan0disabled; 310 | uint8_t m_irq_cpureset; 311 | 312 | uint8_t m_scanned_key; 313 | kbd_row m_kbd_matrix[16]; 314 | 315 | private: 316 | TAPEREADCALLBACK m_TapeReadCallback; 317 | TAPEWRITECALLBACK m_TapeWriteCallback; 318 | int m_nTapeSampleRate; 319 | SOUNDGENCALLBACK m_SoundGenCallback; 320 | int m_SoundPrevValue; ///< Previous value of the sound signal 321 | int m_SoundChanges; ///< Sound signal 0 to 1 changes since the beginning of the frame 322 | SERIALINCALLBACK m_SerialInCallback; 323 | SERIALOUTCALLBACK m_SerialOutCallback; 324 | PARALLELOUTCALLBACK m_ParallelOutCallback; 325 | NETWORKINCALLBACK m_NetworkInCallback; 326 | NETWORKOUTCALLBACK m_NetworkOutCallback; 327 | TERMINALOUTCALLBACK m_TerminalOutCallback; 328 | 329 | void DoSound(); 330 | }; 331 | 332 | inline uint16_t CMotherboard::GetRAMWord(int plan, uint16_t offset) const 333 | { 334 | ASSERT(plan >= 0 && plan <= 2); 335 | return *((uint16_t*)(m_pRAM[plan] + (offset & 0xFFFE))); 336 | } 337 | inline uint8_t CMotherboard::GetRAMByte(int plan, uint16_t offset) const 338 | { 339 | ASSERT(plan >= 0 && plan <= 2); 340 | return m_pRAM[plan][offset]; 341 | } 342 | inline void CMotherboard::SetRAMWord(int plan, uint16_t offset, uint16_t word) 343 | { 344 | ASSERT(plan >= 0 && plan <= 2); 345 | *((uint16_t*)(m_pRAM[plan] + (offset & 0xFFFE))) = word; 346 | } 347 | inline void CMotherboard::SetRAMByte(int plan, uint16_t offset, uint8_t byte) 348 | { 349 | ASSERT(plan >= 0 && plan <= 2); 350 | m_pRAM[plan][offset] = byte; 351 | } 352 | 353 | 354 | ////////////////////////////////////////////////////////////////////// 355 | -------------------------------------------------------------------------------- /emubase/Hard.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Hard.cpp 12 | /// \brief Hard disk drive emulation. 13 | /// \details See defines in header file Emubase.h 14 | 15 | #include "stdafx.h" 16 | #include 17 | #include "Emubase.h" 18 | 19 | 20 | ////////////////////////////////////////////////////////////////////// 21 | // Constants 22 | 23 | #define TIME_PER_SECTOR (IDE_DISK_SECTOR_SIZE / 2) 24 | 25 | #define IDE_PORT_DATA 0x1f0 26 | #define IDE_PORT_ERROR 0x1f1 27 | #define IDE_PORT_SECTOR_COUNT 0x1f2 28 | #define IDE_PORT_SECTOR_NUMBER 0x1f3 29 | #define IDE_PORT_CYLINDER_LSB 0x1f4 30 | #define IDE_PORT_CYLINDER_MSB 0x1f5 31 | #define IDE_PORT_HEAD_NUMBER 0x1f6 32 | #define IDE_PORT_STATUS_COMMAND 0x1f7 33 | 34 | #define IDE_STATUS_ERROR 0x01 35 | #define IDE_STATUS_HIT_INDEX 0x02 36 | #define IDE_STATUS_BUFFER_READY 0x08 37 | #define IDE_STATUS_SEEK_COMPLETE 0x10 38 | #define IDE_STATUS_DRIVE_READY 0x40 39 | #define IDE_STATUS_BUSY 0x80 40 | 41 | #define IDE_COMMAND_READ_MULTIPLE 0x20 42 | #define IDE_COMMAND_READ_MULTIPLE1 0x21 43 | #define IDE_COMMAND_SET_CONFIG 0x91 44 | #define IDE_COMMAND_WRITE_MULTIPLE 0x30 45 | #define IDE_COMMAND_WRITE_MULTIPLE1 0x31 46 | #define IDE_COMMAND_IDENTIFY 0xec 47 | 48 | #define IDE_ERROR_NONE 0x00 49 | #define IDE_ERROR_DEFAULT 0x01 50 | #define IDE_ERROR_UNKNOWN_COMMAND 0x04 51 | #define IDE_ERROR_BAD_LOCATION 0x10 52 | #define IDE_ERROR_BAD_SECTOR 0x80 53 | 54 | enum TimeoutEvent 55 | { 56 | TIMEEVT_NONE = 0, 57 | TIMEEVT_RESET_DONE = 1, 58 | TIMEEVT_READ_SECTOR_DONE = 2, 59 | TIMEEVT_WRITE_SECTOR_DONE = 3, 60 | }; 61 | 62 | ////////////////////////////////////////////////////////////////////// 63 | 64 | // Inverts 512 bytes in the buffer 65 | static void InvertBuffer(void* buffer) 66 | { 67 | uint32_t* p = (uint32_t*) buffer; 68 | for (int i = 0; i < 128; i++) 69 | { 70 | *p = ~(*p); 71 | p++; 72 | } 73 | } 74 | 75 | ////////////////////////////////////////////////////////////////////// 76 | 77 | 78 | CHardDrive::CHardDrive() 79 | { 80 | m_fpFile = nullptr; 81 | 82 | m_status = IDE_STATUS_BUSY; 83 | m_error = IDE_ERROR_NONE; 84 | m_command = 0; 85 | m_timeoutcount = m_timeoutevent = 0; 86 | m_sectorcount = 0; 87 | 88 | m_numsectors = m_numheads = m_numcylinders = 0; 89 | m_curcylinder = m_curhead = m_curheadreg = m_cursector = m_bufferoffset = 0; 90 | memset(m_buffer, 0, IDE_DISK_SECTOR_SIZE); 91 | 92 | m_okInverted = false; 93 | m_okReadOnly = false; 94 | } 95 | 96 | CHardDrive::~CHardDrive() 97 | { 98 | DetachImage(); 99 | } 100 | 101 | void CHardDrive::Reset() 102 | { 103 | // DebugPrint(_T("HDD Reset\r\n")); 104 | 105 | m_status = IDE_STATUS_BUSY; 106 | m_error = IDE_ERROR_NONE; 107 | m_command = 0; 108 | m_timeoutcount = 2; 109 | m_timeoutevent = TIMEEVT_RESET_DONE; 110 | } 111 | 112 | bool CHardDrive::AttachImage(LPCTSTR sFileName) 113 | { 114 | ASSERT(sFileName != nullptr); 115 | 116 | // Open file 117 | m_okReadOnly = false; 118 | m_fpFile = ::_tfopen(sFileName, _T("r+b")); 119 | if (m_fpFile == nullptr) 120 | { 121 | m_okReadOnly = true; 122 | m_fpFile = ::_tfopen(sFileName, _T("rb")); 123 | } 124 | if (m_fpFile == nullptr) 125 | return false; 126 | 127 | // Check file size 128 | ::fseek(m_fpFile, 0, SEEK_END); 129 | uint32_t dwFileSize = ::ftell(m_fpFile); 130 | ::fseek(m_fpFile, 0, SEEK_SET); 131 | if (dwFileSize % 512 != 0) 132 | return false; 133 | 134 | // Read first sector 135 | size_t dwBytesRead = ::fread(m_buffer, 1, 512, m_fpFile); 136 | if (dwBytesRead != 512) 137 | return false; 138 | 139 | // Autodetect inverted image 140 | uint8_t test = 0xff; 141 | for (int i = 0x1f0; i <= 0x1fb; i++) 142 | test &= m_buffer[i]; 143 | m_okInverted = (test == 0xff); 144 | // Invert the buffer if needed 145 | if (m_okInverted) 146 | InvertBuffer(m_buffer); 147 | 148 | // Calculate geometry 149 | m_numsectors = *(m_buffer + 0); 150 | m_numheads = *(m_buffer + 1); 151 | if (m_numsectors == 0 || m_numheads == 0) 152 | return false; // Geometry params are not defined 153 | m_numcylinders = dwFileSize / 512 / m_numsectors / m_numheads; 154 | if (m_numcylinders == 0 || m_numcylinders > 1024) 155 | return false; 156 | 157 | m_curcylinder = m_curhead = m_curheadreg = m_cursector = m_bufferoffset = 0; 158 | 159 | m_status = IDE_STATUS_BUSY; 160 | m_error = IDE_ERROR_NONE; 161 | 162 | return true; 163 | } 164 | 165 | void CHardDrive::DetachImage() 166 | { 167 | if (m_fpFile == nullptr) return; 168 | 169 | //FlushChanges(); 170 | 171 | ::fclose(m_fpFile); 172 | m_fpFile = nullptr; 173 | } 174 | 175 | uint16_t CHardDrive::ReadPort(uint16_t port) 176 | { 177 | ASSERT(port >= 0x1F0 && port <= 0x1F7); 178 | 179 | uint16_t data = 0; 180 | switch (port) 181 | { 182 | case IDE_PORT_DATA: 183 | if (m_status & IDE_STATUS_BUFFER_READY) 184 | { 185 | data = *((uint16_t*)(m_buffer + m_bufferoffset)); 186 | if (!m_okInverted) 187 | data = ~data; // Image stored non-inverted, but QBUS inverts the bits 188 | m_bufferoffset += 2; 189 | 190 | if (m_bufferoffset >= IDE_DISK_SECTOR_SIZE) 191 | { 192 | // DebugPrint(_T("HDD Read sector complete\r\n")); 193 | 194 | ContinueRead(); 195 | } 196 | } 197 | break; 198 | case IDE_PORT_ERROR: 199 | data = 0xff00 | m_error; 200 | break; 201 | case IDE_PORT_SECTOR_COUNT: 202 | data = 0xff00 | (uint16_t)m_sectorcount; 203 | break; 204 | case IDE_PORT_SECTOR_NUMBER: 205 | data = 0xff00 | (uint16_t)m_cursector; 206 | break; 207 | case IDE_PORT_CYLINDER_LSB: 208 | data = 0xff00 | (uint16_t)(m_curcylinder & 0xff); 209 | break; 210 | case IDE_PORT_CYLINDER_MSB: 211 | data = 0xff00 | (uint16_t)((m_curcylinder >> 8) & 0xff); 212 | break; 213 | case IDE_PORT_HEAD_NUMBER: 214 | data = 0xff00 | (uint16_t)m_curheadreg; 215 | break; 216 | case IDE_PORT_STATUS_COMMAND: 217 | data = 0xff00 | m_status; 218 | break; 219 | } 220 | 221 | //DebugPrintFormat(_T("HDD Read %x 0x%04x\r\n"), port, data); 222 | return data; 223 | } 224 | 225 | void CHardDrive::WritePort(uint16_t port, uint16_t data) 226 | { 227 | ASSERT(port >= 0x1F0 && port <= 0x1F7); 228 | 229 | // DebugPrintFormat(_T("HDD Write %x <-- 0x%04x\r\n"), port, data); 230 | 231 | switch (port) 232 | { 233 | case IDE_PORT_DATA: 234 | if (m_status & IDE_STATUS_BUFFER_READY) 235 | { 236 | if (!m_okInverted) 237 | data = ~data; // Image stored non-inverted, but QBUS inverts the bits 238 | *((uint16_t*)(m_buffer + m_bufferoffset)) = data; 239 | m_bufferoffset += 2; 240 | 241 | if (m_bufferoffset >= IDE_DISK_SECTOR_SIZE) 242 | { 243 | m_status &= ~IDE_STATUS_BUFFER_READY; 244 | 245 | ContinueWrite(); 246 | } 247 | } 248 | break; 249 | case IDE_PORT_ERROR: 250 | // Writing precompensation value -- ignore 251 | break; 252 | case IDE_PORT_SECTOR_COUNT: 253 | data &= 0x0ff; 254 | m_sectorcount = (data == 0) ? 256 : data; 255 | break; 256 | case IDE_PORT_SECTOR_NUMBER: 257 | data &= 0x0ff; 258 | m_cursector = data; 259 | break; 260 | case IDE_PORT_CYLINDER_LSB: 261 | data &= 0x0ff; 262 | m_curcylinder = (m_curcylinder & 0xff00) | (data & 0xff); 263 | break; 264 | case IDE_PORT_CYLINDER_MSB: 265 | data &= 0x0ff; 266 | m_curcylinder = (m_curcylinder & 0x00ff) | ((data & 0xff) << 8); 267 | break; 268 | case IDE_PORT_HEAD_NUMBER: 269 | data &= 0x0ff; 270 | m_curhead = data & 0x0f; 271 | m_curheadreg = data; 272 | break; 273 | case IDE_PORT_STATUS_COMMAND: 274 | data &= 0x0ff; 275 | HandleCommand((uint8_t)data); 276 | break; 277 | } 278 | } 279 | 280 | // Called from CMotherboard::SystemFrame() every tick 281 | void CHardDrive::Periodic() 282 | { 283 | if (m_timeoutcount > 0) 284 | { 285 | m_timeoutcount--; 286 | if (m_timeoutcount == 0) 287 | { 288 | int evt = m_timeoutevent; 289 | m_timeoutevent = TIMEEVT_NONE; 290 | switch (evt) 291 | { 292 | case TIMEEVT_RESET_DONE: 293 | m_status &= ~IDE_STATUS_BUSY; 294 | m_status |= IDE_STATUS_DRIVE_READY | IDE_STATUS_SEEK_COMPLETE; 295 | break; 296 | case TIMEEVT_READ_SECTOR_DONE: 297 | ReadSectorDone(); 298 | break; 299 | case TIMEEVT_WRITE_SECTOR_DONE: 300 | WriteSectorDone(); 301 | break; 302 | } 303 | } 304 | } 305 | } 306 | 307 | void CHardDrive::HandleCommand(uint8_t command) 308 | { 309 | m_command = command; 310 | switch (command) 311 | { 312 | case IDE_COMMAND_READ_MULTIPLE: 313 | case IDE_COMMAND_READ_MULTIPLE1: 314 | // DebugPrintFormat(_T("HDD COMMAND %02x (READ MULT): C=%d, H=%d, SN=%d, SC=%d\r\n"), 315 | // command, m_curcylinder, m_curhead, m_cursector, m_sectorcount); 316 | 317 | m_status |= IDE_STATUS_BUSY; 318 | 319 | m_timeoutcount = TIME_PER_SECTOR * 3; // Timeout while seek for track 320 | m_timeoutevent = TIMEEVT_READ_SECTOR_DONE; 321 | break; 322 | 323 | case IDE_COMMAND_SET_CONFIG: 324 | // DebugPrintFormat(_T("HDD COMMAND %02x (SET CONFIG): H=%d, SC=%d\r\n"), 325 | // command, m_curhead, m_sectorcount); 326 | 327 | m_numsectors = m_sectorcount; 328 | m_numheads = m_curhead + 1; 329 | break; 330 | 331 | case IDE_COMMAND_WRITE_MULTIPLE: 332 | case IDE_COMMAND_WRITE_MULTIPLE1: 333 | // DebugPrintFormat(_T("HDD COMMAND %02x (WRITE MULT): C=%d, H=%d, SN=%d, SC=%d\r\n"), 334 | // command, m_curcylinder, m_curhead, m_cursector, m_sectorcount); 335 | 336 | m_bufferoffset = 0; 337 | m_status |= IDE_STATUS_BUFFER_READY; 338 | break; 339 | 340 | case IDE_COMMAND_IDENTIFY: 341 | // DebugPrintFormat(_T("HDD COMMAND %02x (IDENTIFY)\r\n"), command); 342 | 343 | IdentifyDrive(); // Prepare the buffer 344 | m_bufferoffset = 0; 345 | m_sectorcount = 1; 346 | m_status |= IDE_STATUS_BUFFER_READY | IDE_STATUS_SEEK_COMPLETE | IDE_STATUS_DRIVE_READY; 347 | m_status &= ~IDE_STATUS_BUSY; 348 | m_status &= ~IDE_STATUS_ERROR; 349 | break; 350 | 351 | default: 352 | // DebugPrintFormat(_T("HDD COMMAND %02x (UNKNOWN): C=%d, H=%d, SN=%d, SC=%d\r\n"), 353 | // command, m_curcylinder, m_curhead, m_cursector, m_sectorcount); 354 | // //DebugBreak(); // Implement this IDE command! 355 | 356 | break; 357 | } 358 | } 359 | 360 | // Copy the string to the destination, swapping bytes in every word 361 | // For use in CHardDrive::IdentifyDrive() method. 362 | static void swap_strncpy(uint8_t* dst, const char* src, int words) 363 | { 364 | int i; 365 | for (i = 0; src[i] != 0; i++) 366 | dst[i ^ 1] = src[i]; 367 | for ( ; i < words * 2; i++) 368 | dst[i ^ 1] = ' '; 369 | } 370 | 371 | void CHardDrive::IdentifyDrive() 372 | { 373 | uint32_t totalsectors = (uint32_t)m_numcylinders * (uint32_t)m_numheads * (uint32_t)m_numsectors; 374 | 375 | memset(m_buffer, 0, IDE_DISK_SECTOR_SIZE); 376 | 377 | uint16_t* pwBuffer = (uint16_t*)m_buffer; 378 | pwBuffer[0] = 0x045a; // Configuration: fixed disk 379 | pwBuffer[1] = (uint16_t)m_numcylinders; 380 | pwBuffer[3] = (uint16_t)m_numheads; 381 | pwBuffer[6] = (uint16_t)m_numsectors; 382 | swap_strncpy((uint8_t*)(pwBuffer + 10), "0000000000", 10); // Serial number 383 | swap_strncpy((uint8_t*)(pwBuffer + 23), "1.0", 4); // Firmware version 384 | swap_strncpy((uint8_t*)(pwBuffer + 27), "UKNCBTL Hard Disk", 18); // Model 385 | pwBuffer[47] = 0x8001; // Read/write multiple support 386 | pwBuffer[49] = 0x2f00; // Capabilities: bit9 = LBA 387 | pwBuffer[53] = 1; // Words 54-58 are valid 388 | pwBuffer[54] = (uint16_t)m_numcylinders; 389 | pwBuffer[55] = (uint16_t)m_numheads; 390 | pwBuffer[56] = (uint16_t)m_numsectors; 391 | *(uint32_t*)(pwBuffer + 57) = (uint32_t)m_numheads * (uint32_t)m_numsectors; 392 | *(uint32_t*)(pwBuffer + 60) = totalsectors; 393 | *(uint32_t*)(pwBuffer + 100) = totalsectors; 394 | 395 | InvertBuffer(m_buffer); 396 | } 397 | 398 | uint32_t CHardDrive::CalculateOffset() const 399 | { 400 | int sector = (m_curcylinder * m_numheads + m_curhead) * m_numsectors + (m_cursector - 1); 401 | return sector * IDE_DISK_SECTOR_SIZE; 402 | } 403 | 404 | void CHardDrive::ReadNextSector() 405 | { 406 | m_status |= IDE_STATUS_BUSY; 407 | 408 | m_timeoutcount = TIME_PER_SECTOR * 2; // Timeout while seek for next sector 409 | m_timeoutevent = TIMEEVT_READ_SECTOR_DONE; 410 | } 411 | 412 | void CHardDrive::ReadSectorDone() 413 | { 414 | m_status &= ~IDE_STATUS_BUSY; 415 | m_status &= ~IDE_STATUS_ERROR; 416 | m_status |= IDE_STATUS_BUFFER_READY; 417 | m_status |= IDE_STATUS_SEEK_COMPLETE; 418 | 419 | // Read sector from HDD image to the buffer 420 | uint32_t fileOffset = CalculateOffset(); 421 | ::fseek(m_fpFile, fileOffset, SEEK_SET); 422 | size_t dwBytesRead = ::fread(m_buffer, 1, IDE_DISK_SECTOR_SIZE, m_fpFile); 423 | if (dwBytesRead != IDE_DISK_SECTOR_SIZE) 424 | { 425 | m_status |= IDE_STATUS_ERROR; 426 | m_error = IDE_ERROR_BAD_SECTOR; 427 | return; 428 | } 429 | 430 | if (m_sectorcount > 0) 431 | m_sectorcount--; 432 | 433 | if (m_sectorcount > 0) 434 | { 435 | NextSector(); 436 | } 437 | 438 | m_error = IDE_ERROR_NONE; 439 | m_bufferoffset = 0; 440 | } 441 | 442 | void CHardDrive::WriteSectorDone() 443 | { 444 | m_status &= ~IDE_STATUS_BUSY; 445 | m_status &= ~IDE_STATUS_ERROR; 446 | m_status |= IDE_STATUS_BUFFER_READY; 447 | m_status |= IDE_STATUS_SEEK_COMPLETE; 448 | 449 | // Write buffer to the HDD image 450 | uint32_t fileOffset = CalculateOffset(); 451 | 452 | // DebugPrintFormat(_T("WriteSector %lx\r\n"), fileOffset); 453 | 454 | if (m_okReadOnly) 455 | { 456 | m_status |= IDE_STATUS_ERROR; 457 | m_error = IDE_ERROR_BAD_SECTOR; 458 | return; 459 | } 460 | 461 | ::fseek(m_fpFile, fileOffset, SEEK_SET); 462 | size_t dwBytesWritten = ::fwrite(m_buffer, 1, IDE_DISK_SECTOR_SIZE, m_fpFile); 463 | if (dwBytesWritten != IDE_DISK_SECTOR_SIZE) 464 | { 465 | m_status |= IDE_STATUS_ERROR; 466 | m_error = IDE_ERROR_BAD_SECTOR; 467 | return; 468 | } 469 | 470 | if (m_sectorcount > 0) 471 | m_sectorcount--; 472 | 473 | if (m_sectorcount > 0) 474 | { 475 | NextSector(); 476 | } 477 | 478 | m_error = IDE_ERROR_NONE; 479 | m_bufferoffset = 0; 480 | } 481 | 482 | void CHardDrive::NextSector() 483 | { 484 | // Advance to the next sector, CHS-based 485 | m_cursector++; 486 | if (m_cursector > m_numsectors) // Sectors are 1-based 487 | { 488 | m_cursector = 1; 489 | m_curhead++; 490 | if (m_curhead >= m_numheads) 491 | { 492 | m_curhead = 0; 493 | m_curcylinder++; 494 | } 495 | } 496 | } 497 | 498 | void CHardDrive::ContinueRead() 499 | { 500 | m_bufferoffset = 0; 501 | 502 | m_status &= ~IDE_STATUS_BUFFER_READY; 503 | m_status &= ~IDE_STATUS_BUSY; 504 | 505 | if (m_sectorcount > 0) 506 | ReadNextSector(); 507 | } 508 | 509 | void CHardDrive::ContinueWrite() 510 | { 511 | m_bufferoffset = 0; 512 | 513 | m_status &= ~IDE_STATUS_BUFFER_READY; 514 | m_status |= IDE_STATUS_BUSY; 515 | 516 | m_timeoutcount = TIME_PER_SECTOR; 517 | m_timeoutevent = TIMEEVT_WRITE_SECTOR_DONE; 518 | } 519 | 520 | 521 | ////////////////////////////////////////////////////////////////////// 522 | -------------------------------------------------------------------------------- /emubase/Disasm.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Disasm.cpp 12 | /// \brief Disassembler for KM1801VM2 processor 13 | /// \details See defines in header file Emubase.h 14 | 15 | #include "stdafx.h" 16 | #include "Defines.h" 17 | 18 | 19 | // Формат отображения режимов адресации 20 | const LPCTSTR ADDRESS_MODE_FORMAT[] = 21 | { 22 | _T("%s"), _T("(%s)"), _T("(%s)+"), _T("@(%s)+"), _T("-(%s)"), _T("@-(%s)"), _T("%06o(%s)"), _T("@%06o(%s)") 23 | }; 24 | // Формат отображения режимов адресации для регистра PC 25 | const LPCTSTR ADDRESS_MODE_PC_FORMAT[] = 26 | { 27 | _T("PC"), _T("(PC)"), _T("#%06o"), _T("@#%06o"), _T("-(PC)"), _T("@-(PC)"), _T("%06o"), _T("@%06o") 28 | }; 29 | 30 | // strSrc - at least 24 characters 31 | uint16_t ConvertSrcToString(uint16_t instr, uint16_t addr, TCHAR* strSrc, uint16_t code) 32 | { 33 | const size_t strSrcSize = 24; 34 | 35 | uint8_t reg = GetDigit(instr, 2); 36 | uint8_t param = GetDigit(instr, 3); 37 | 38 | LPCTSTR pszReg = REGISTER_NAME[reg]; 39 | 40 | if (reg != 7) 41 | { 42 | LPCTSTR format = ADDRESS_MODE_FORMAT[param]; 43 | 44 | if (param == 6 || param == 7) 45 | { 46 | uint16_t word = code; //TODO: pMemory 47 | _sntprintf(strSrc, strSrcSize - 1, format, word, pszReg); 48 | return 1; 49 | } 50 | else 51 | _sntprintf(strSrc, strSrcSize - 1, format, pszReg); 52 | } 53 | else 54 | { 55 | LPCTSTR format = ADDRESS_MODE_PC_FORMAT[param]; 56 | 57 | if (param == 2 || param == 3) 58 | { 59 | uint16_t word = code; //TODO: pMemory 60 | _sntprintf(strSrc, strSrcSize - 1, format, word); 61 | return 1; 62 | } 63 | else if (param == 6 || param == 7) 64 | { 65 | uint16_t word = code; //TODO: pMemory 66 | _sntprintf(strSrc, strSrcSize - 1, format, (uint16_t)(addr + word + 2)); 67 | return 1; 68 | } 69 | else 70 | _sntprintf(strSrc, strSrcSize - 1, format, pszReg); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | // strDst - at least 24 characters 77 | uint16_t ConvertDstToString (uint16_t instr, uint16_t addr, TCHAR* strDst, uint16_t code) 78 | { 79 | const size_t strDstSize = 24; 80 | 81 | uint8_t reg = GetDigit(instr, 0); 82 | uint8_t param = GetDigit(instr, 1); 83 | 84 | LPCTSTR pszReg = REGISTER_NAME[reg]; 85 | 86 | if (reg != 7) 87 | { 88 | LPCTSTR format = ADDRESS_MODE_FORMAT[param]; 89 | 90 | if (param == 6 || param == 7) 91 | { 92 | _sntprintf(strDst, strDstSize - 1, format, code, pszReg); 93 | return 1; 94 | } 95 | else 96 | _sntprintf(strDst, strDstSize - 1, format, pszReg); 97 | } 98 | else 99 | { 100 | LPCTSTR format = ADDRESS_MODE_PC_FORMAT[param]; 101 | 102 | if (param == 2 || param == 3) 103 | { 104 | _sntprintf(strDst, strDstSize - 1, format, code); 105 | return 1; 106 | } 107 | else if (param == 6 || param == 7) 108 | { 109 | _sntprintf(strDst, strDstSize - 1, format, (uint16_t)(addr + code + 2)); 110 | return 1; 111 | } 112 | else 113 | _sntprintf(strDst, strDstSize - 1, format, pszReg); 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | // Disassemble one instruction 120 | // pMemory - memory image (we read only words of the instruction) 121 | // strInstr - instruction mnemonics buffer, at least 8 characters 122 | // strArg - instruction arguments buffer, at least 32 characters 123 | // Return value: number of words in the instruction 124 | uint16_t DisassembleInstruction(const uint16_t* pMemory, uint16_t addr, TCHAR* strInstr, TCHAR* strArg) 125 | { 126 | //const size_t strInstrSize = 8; 127 | const size_t strArgSize = 32; 128 | 129 | *strInstr = 0; 130 | *strArg = 0; 131 | 132 | uint16_t instr = *pMemory; 133 | uint16_t length = 1; 134 | LPCTSTR strReg = nullptr; 135 | 136 | const size_t strSrcSize = 24; 137 | TCHAR strSrc[strSrcSize]; 138 | const size_t strDstSize = 24; 139 | TCHAR strDst[strDstSize]; 140 | 141 | bool okByte; 142 | 143 | // No fields 144 | switch (instr) 145 | { 146 | case PI_HALT: _tcscpy(strInstr, _T("HALT")); return 1; 147 | case PI_WAIT: _tcscpy(strInstr, _T("WAIT")); return 1; 148 | case PI_RTI: _tcscpy(strInstr, _T("RTI")); return 1; 149 | case PI_BPT: _tcscpy(strInstr, _T("BPT")); return 1; 150 | case PI_IOT: _tcscpy(strInstr, _T("IOT")); return 1; 151 | case PI_RESET: _tcscpy(strInstr, _T("RESET")); return 1; 152 | case PI_RTT: _tcscpy(strInstr, _T("RTT")); return 1; 153 | case PI_NOP: _tcscpy(strInstr, _T("NOP")); return 1; 154 | case PI_CLC: _tcscpy(strInstr, _T("CLC")); return 1; 155 | case PI_CLV: _tcscpy(strInstr, _T("CLV")); return 1; 156 | case PI_CLVC: _tcscpy(strInstr, _T("CLVC")); return 1; 157 | case PI_CLZ: _tcscpy(strInstr, _T("CLZ")); return 1; 158 | case PI_CLZC: _tcscpy(strInstr, _T("CLZC")); return 1; 159 | case PI_CLZV: _tcscpy(strInstr, _T("CLZV")); return 1; 160 | case PI_CLZVC: _tcscpy(strInstr, _T("CLZVC")); return 1; 161 | case PI_CLN: _tcscpy(strInstr, _T("CLN")); return 1; 162 | case PI_CLNC: _tcscpy(strInstr, _T("CLNC")); return 1; 163 | case PI_CLNV: _tcscpy(strInstr, _T("CLNV")); return 1; 164 | case PI_CLNVC: _tcscpy(strInstr, _T("CLNVC")); return 1; 165 | case PI_CLNZ: _tcscpy(strInstr, _T("CLNZ")); return 1; 166 | case PI_CLNZC: _tcscpy(strInstr, _T("CLNZC")); return 1; 167 | case PI_CLNZV: _tcscpy(strInstr, _T("CLNZV")); return 1; 168 | case PI_CCC: _tcscpy(strInstr, _T("CCC")); return 1; 169 | case PI_NOP260: _tcscpy(strInstr, _T("NOP260")); return 1; 170 | case PI_SEC: _tcscpy(strInstr, _T("SEC")); return 1; 171 | case PI_SEV: _tcscpy(strInstr, _T("SEV")); return 1; 172 | case PI_SEVC: _tcscpy(strInstr, _T("SEVC")); return 1; 173 | case PI_SEZ: _tcscpy(strInstr, _T("SEZ")); return 1; 174 | case PI_SEZC: _tcscpy(strInstr, _T("SEZC")); return 1; 175 | case PI_SEZV: _tcscpy(strInstr, _T("SEZV")); return 1; 176 | case PI_SEZVC: _tcscpy(strInstr, _T("SEZVC")); return 1; 177 | case PI_SEN: _tcscpy(strInstr, _T("SEN")); return 1; 178 | case PI_SENC: _tcscpy(strInstr, _T("SENC")); return 1; 179 | case PI_SENV: _tcscpy(strInstr, _T("SENV")); return 1; 180 | case PI_SENVC: _tcscpy(strInstr, _T("SENVC")); return 1; 181 | case PI_SENZ: _tcscpy(strInstr, _T("SENZ")); return 1; 182 | case PI_SENZC: _tcscpy(strInstr, _T("SENZC")); return 1; 183 | case PI_SENZV: _tcscpy(strInstr, _T("SENZV")); return 1; 184 | case PI_SCC: _tcscpy(strInstr, _T("SCC")); return 1; 185 | 186 | // Спецкоманды режима HALT ВМ2 187 | case PI_START: _tcscpy(strInstr, _T("START")); return 1; 188 | case PI_STEP: _tcscpy(strInstr, _T("STEP")); return 1; 189 | case PI_RSEL: _tcscpy(strInstr, _T("RSEL")); return 1; 190 | case PI_MFUS: _tcscpy(strInstr, _T("MFUS")); return 1; 191 | case PI_RCPC: _tcscpy(strInstr, _T("RCPC")); return 1; 192 | case PI_RCPS: _tcscpy(strInstr, _T("RCPS")); return 1; 193 | case PI_MTUS: _tcscpy(strInstr, _T("MTUS")); return 1; 194 | case PI_WCPC: _tcscpy(strInstr, _T("WCPC")); return 1; 195 | case PI_WCPS: _tcscpy(strInstr, _T("WCPS")); return 1; 196 | } 197 | 198 | // One field 199 | if ((instr & ~(uint16_t)7) == PI_RTS) 200 | { 201 | if (GetDigit(instr, 0) == 7) 202 | { 203 | _tcscpy(strInstr, _T("RETURN")); 204 | } 205 | else 206 | { 207 | _tcscpy(strInstr, _T("RTS")); 208 | _tcscpy(strArg, REGISTER_NAME[GetDigit(instr, 0)]); 209 | } 210 | return 1; 211 | } 212 | 213 | // Two fields 214 | length += ConvertDstToString(instr, addr + 2, strDst, pMemory[1]); 215 | 216 | switch (instr & ~(uint16_t)077) 217 | { 218 | case PI_JMP: _tcscpy(strInstr, _T("JMP")); _tcscpy(strArg, strDst); return length; 219 | case PI_SWAB: _tcscpy(strInstr, _T("SWAB")); _tcscpy(strArg, strDst); return length; 220 | case PI_MARK: _tcscpy(strInstr, _T("MARK")); _tcscpy(strArg, strDst); return length; 221 | case PI_SXT: _tcscpy(strInstr, _T("SXT")); _tcscpy(strArg, strDst); return length; 222 | case PI_MTPS: _tcscpy(strInstr, _T("MTPS")); _tcscpy(strArg, strDst); return length; 223 | case PI_MFPS: _tcscpy(strInstr, _T("MFPS")); _tcscpy(strArg, strDst); return length; 224 | } 225 | 226 | okByte = (instr & 0100000) != 0; 227 | 228 | switch (instr & ~(uint16_t)0100077) 229 | { 230 | case PI_CLR: _tcscpy(strInstr, okByte ? _T("CLRB") : _T("CLR")); _tcscpy(strArg, strDst); return length; 231 | case PI_COM: _tcscpy(strInstr, okByte ? _T("COMB") : _T("COM")); _tcscpy(strArg, strDst); return length; 232 | case PI_INC: _tcscpy(strInstr, okByte ? _T("INCB") : _T("INC")); _tcscpy(strArg, strDst); return length; 233 | case PI_DEC: _tcscpy(strInstr, okByte ? _T("DECB") : _T("DEC")); _tcscpy(strArg, strDst); return length; 234 | case PI_NEG: _tcscpy(strInstr, okByte ? _T("NEGB") : _T("NEG")); _tcscpy(strArg, strDst); return length; 235 | case PI_ADC: _tcscpy(strInstr, okByte ? _T("ADCB") : _T("ADC")); _tcscpy(strArg, strDst); return length; 236 | case PI_SBC: _tcscpy(strInstr, okByte ? _T("SBCB") : _T("SBC")); _tcscpy(strArg, strDst); return length; 237 | case PI_TST: _tcscpy(strInstr, okByte ? _T("TSTB") : _T("TST")); _tcscpy(strArg, strDst); return length; 238 | case PI_ROR: _tcscpy(strInstr, okByte ? _T("RORB") : _T("ROR")); _tcscpy(strArg, strDst); return length; 239 | case PI_ROL: _tcscpy(strInstr, okByte ? _T("ROLB") : _T("ROL")); _tcscpy(strArg, strDst); return length; 240 | case PI_ASR: _tcscpy(strInstr, okByte ? _T("ASRB") : _T("ASR")); _tcscpy(strArg, strDst); return length; 241 | case PI_ASL: _tcscpy(strInstr, okByte ? _T("ASLB") : _T("ASL")); _tcscpy(strArg, strDst); return length; 242 | } 243 | 244 | length = 1; 245 | _sntprintf(strDst, strDstSize - 1, _T("%06o"), addr + ((short)(char)(uint8_t)(instr & 0xff) * 2) + 2); 246 | 247 | // Branchs & interrupts 248 | switch (instr & ~(uint16_t)0377) 249 | { 250 | case PI_BR: _tcscpy(strInstr, _T("BR")); _tcscpy(strArg, strDst); return 1; 251 | case PI_BNE: _tcscpy(strInstr, _T("BNE")); _tcscpy(strArg, strDst); return 1; 252 | case PI_BEQ: _tcscpy(strInstr, _T("BEQ")); _tcscpy(strArg, strDst); return 1; 253 | case PI_BGE: _tcscpy(strInstr, _T("BGE")); _tcscpy(strArg, strDst); return 1; 254 | case PI_BLT: _tcscpy(strInstr, _T("BLT")); _tcscpy(strArg, strDst); return 1; 255 | case PI_BGT: _tcscpy(strInstr, _T("BGT")); _tcscpy(strArg, strDst); return 1; 256 | case PI_BLE: _tcscpy(strInstr, _T("BLE")); _tcscpy(strArg, strDst); return 1; 257 | case PI_BPL: _tcscpy(strInstr, _T("BPL")); _tcscpy(strArg, strDst); return 1; 258 | case PI_BMI: _tcscpy(strInstr, _T("BMI")); _tcscpy(strArg, strDst); return 1; 259 | case PI_BHI: _tcscpy(strInstr, _T("BHI")); _tcscpy(strArg, strDst); return 1; 260 | case PI_BLOS: _tcscpy(strInstr, _T("BLOS")); _tcscpy(strArg, strDst); return 1; 261 | case PI_BVC: _tcscpy(strInstr, _T("BVC")); _tcscpy(strArg, strDst); return 1; 262 | case PI_BVS: _tcscpy(strInstr, _T("BVS")); _tcscpy(strArg, strDst); return 1; 263 | case PI_BHIS: _tcscpy(strInstr, _T("BHIS")); _tcscpy(strArg, strDst); return 1; 264 | case PI_BLO: _tcscpy(strInstr, _T("BLO")); _tcscpy(strArg, strDst); return 1; 265 | } 266 | 267 | _sntprintf(strDst, strDstSize - 1, _T("%06o"), (uint8_t)(instr & 0xff)); 268 | 269 | switch (instr & ~(uint16_t)0377) 270 | { 271 | case PI_EMT: _tcscpy(strInstr, _T("EMT")); _tcscpy(strArg, strDst + 3); return 1; 272 | case PI_TRAP: _tcscpy(strInstr, _T("TRAP")); _tcscpy(strArg, strDst + 3); return 1; 273 | } 274 | 275 | // Three fields 276 | switch (instr & ~(uint16_t)0777) 277 | { 278 | case PI_JSR: 279 | if (GetDigit(instr, 2) == 7) 280 | { 281 | _tcscpy(strInstr, _T("CALL")); 282 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 283 | _sntprintf(strArg, strArgSize - 1, _T("%s"), strDst); // strArg = strDst; 284 | } 285 | else 286 | { 287 | _tcscpy(strInstr, _T("JSR")); 288 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 289 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 290 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strReg, strDst); // strArg = strReg + ", " + strDst; 291 | } 292 | return length; 293 | case PI_MUL: 294 | _tcscpy(strInstr, _T("MUL")); 295 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 296 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 297 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strDst, strReg); // strArg = strDst + ", " + strReg; 298 | return length; 299 | case PI_DIV: 300 | _tcscpy(strInstr, _T("DIV")); 301 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 302 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 303 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strDst, strReg); // strArg = strDst + ", " + strReg; 304 | return length; 305 | case PI_ASH: 306 | _tcscpy(strInstr, _T("ASH")); 307 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 308 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 309 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strDst, strReg); // strArg = strDst + ", " + strReg; 310 | return length; 311 | case PI_ASHC: 312 | _tcscpy(strInstr, _T("ASHC")); 313 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 314 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 315 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strDst, strReg); // strArg = strDst + ", " + strReg; 316 | return length; 317 | case PI_XOR: 318 | _tcscpy(strInstr, _T("XOR")); 319 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 320 | length += ConvertDstToString (instr, addr + 2, strDst, pMemory[1]); 321 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strReg, strDst); // strArg = strReg + ", " + strDst; 322 | return length; 323 | case PI_SOB: 324 | _tcscpy(strInstr, _T("SOB")); 325 | strReg = REGISTER_NAME[GetDigit(instr, 2)]; 326 | _sntprintf(strDst, strDstSize - 1, _T("%06o"), addr - (GetDigit(instr, 1) * 8 + GetDigit(instr, 0)) * 2 + 2); 327 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strReg, strDst); // strArg = strReg + ", " + strDst; 328 | return 1; 329 | } 330 | 331 | // Four fields 332 | 333 | okByte = (instr & 0100000) != 0; 334 | 335 | length += ConvertSrcToString(instr, addr + 2, strSrc, pMemory[1]); 336 | length += ConvertDstToString(instr, (uint16_t)(addr + 2 + (length - 1) * 2), strDst, pMemory[length]); 337 | 338 | switch (instr & ~(uint16_t)0107777) 339 | { 340 | case PI_MOV: 341 | _tcscpy(strInstr, okByte ? _T("MOVB") : _T("MOV")); 342 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 343 | return length; 344 | case PI_CMP: 345 | _tcscpy(strInstr, okByte ? _T("CMPB") : _T("CMP")); 346 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 347 | return length; 348 | case PI_BIT: 349 | _tcscpy(strInstr, okByte ? _T("BITB") : _T("BIT")); 350 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 351 | return length; 352 | case PI_BIC: 353 | _tcscpy(strInstr, okByte ? _T("BICB") : _T("BIC")); 354 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 355 | return length; 356 | case PI_BIS: 357 | _tcscpy(strInstr, okByte ? _T("BISB") : _T("BIS")); 358 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 359 | return length; 360 | } 361 | 362 | switch (instr & ~(uint16_t)0007777) 363 | { 364 | case PI_ADD: 365 | _tcscpy(strInstr, _T("ADD")); 366 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 367 | return length; 368 | case PI_SUB: 369 | _tcscpy(strInstr, _T("SUB")); 370 | _sntprintf(strArg, strArgSize - 1, _T("%s, %s"), strSrc, strDst); // strArg = strSrc + ", " + strDst; 371 | return length; 372 | } 373 | 374 | _tcscpy(strInstr, _T("unknown")); 375 | _sntprintf(strArg, strArgSize - 1, _T("%06o"), instr); 376 | return 1; 377 | } 378 | -------------------------------------------------------------------------------- /emubase/Processor.h: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Processor.h KM1801VM2 processor class 12 | 13 | #pragma once 14 | 15 | #include "Defines.h" 16 | #include "Memory.h" 17 | 18 | 19 | class CMemoryController; 20 | 21 | ////////////////////////////////////////////////////////////////////// 22 | 23 | /// \brief KM1801VM2 processor 24 | class CProcessor 25 | { 26 | public: // Constructor / initialization 27 | CProcessor(LPCTSTR name); 28 | /// \brief Link the processor and memory controller 29 | void AttachMemoryController(CMemoryController* ctl) { m_pMemoryController = ctl; } 30 | void SetHALTPin(bool value) { m_haltpin = value; } 31 | void SetDCLOPin(bool value); 32 | void SetACLOPin(bool value); 33 | void MemoryError(); 34 | /// \brief Get the processor name, assigned in the constructor 35 | LPCTSTR GetName() const { return m_name; } 36 | 37 | public: 38 | static void Init(); ///< Initialize static tables 39 | static void Done(); ///< Release memory used for static tables 40 | protected: // Statics 41 | typedef void ( CProcessor::*ExecuteMethodRef )(); 42 | static ExecuteMethodRef* m_pExecuteMethodMap; 43 | 44 | protected: // Processor state 45 | TCHAR m_name[5]; ///< Processor name (DO NOT use it inside the processor code!!!) 46 | uint16_t m_internalTick; ///< How many ticks waiting to the end of current instruction 47 | uint16_t m_psw; ///< Processor Status Word (PSW) 48 | uint16_t m_R[8]; ///< Registers (R0..R5, R6=SP, R7=PC) 49 | uint16_t m_savepc; ///< CPC register 50 | uint16_t m_savepsw; ///< CPSW register 51 | bool m_okStopped; ///< "Processor stopped" flag 52 | bool m_stepmode; ///< Read true if it's step mode 53 | bool m_buserror; ///< Read true if occured bus error for implementing double bus error if needed 54 | bool m_haltpin; ///< HALT pin 55 | bool m_DCLOpin; ///< DCLO pin 56 | bool m_ACLOpin; ///< ACLO pin 57 | bool m_waitmode; ///< WAIT 58 | 59 | protected: // Current instruction processing 60 | uint16_t m_instruction; ///< Curent instruction 61 | uint16_t m_instructionpc; ///< Address of the current instruction 62 | uint8_t m_regsrc; ///< Source register number 63 | uint8_t m_methsrc; ///< Source address mode 64 | uint16_t m_addrsrc; ///< Source address 65 | uint8_t m_regdest; ///< Destination register number 66 | uint8_t m_methdest; ///< Destination address mode 67 | uint16_t m_addrdest; ///< Destination address 68 | protected: // Interrupt processing 69 | bool m_STRTrq; ///< Start interrupt pending 70 | bool m_RPLYrq; ///< Hangup interrupt pending 71 | bool m_ILLGrq; ///< Illegal instruction interrupt pending 72 | bool m_RSVDrq; ///< Reserved instruction interrupt pending 73 | bool m_TBITrq; ///< T-bit interrupt pending 74 | bool m_ACLOrq; ///< Power down interrupt pending 75 | bool m_HALTrq; ///< HALT command or HALT signal 76 | bool m_EVNTrq; ///< Timer event interrupt pending 77 | bool m_FIS_rq; ///< FIS command interrupt pending 78 | bool m_BPT_rq; ///< BPT command interrupt pending 79 | bool m_IOT_rq; ///< IOT command interrupt pending 80 | bool m_EMT_rq; ///< EMT command interrupt pending 81 | bool m_TRAPrq; ///< TRAP command interrupt pending 82 | uint16_t m_virq[16]; ///< VIRQ vector 83 | bool m_ACLOreset; ///< Power fail interrupt request reset 84 | bool m_EVNTreset; ///< EVNT interrupt request reset; 85 | uint8_t m_VIRQreset; ///< VIRQ request reset for given device 86 | protected: 87 | CMemoryController* m_pMemoryController; 88 | bool m_okTrace; ///< Trace mode on/off 89 | 90 | public: 91 | CMemoryController* GetMemoryController() { return m_pMemoryController; } 92 | 93 | public: // Register control 94 | uint16_t GetPSW() const { return m_psw; } ///< Get the processor status word register value 95 | uint16_t GetCPSW() const { return m_savepsw; } 96 | uint8_t GetLPSW() const { return (uint8_t)(m_psw & 0xff); } ///< Get PSW lower byte 97 | void SetPSW(uint16_t word); ///< Set the processor status word register value 98 | void SetCPSW(uint16_t word) { m_savepsw = word; } 99 | void SetLPSW(uint8_t byte); 100 | uint16_t GetReg(int regno) const { return m_R[regno]; } ///< Get register value, regno=0..7 101 | void SetReg(int regno, uint16_t word); ///< Set register value 102 | uint8_t GetLReg(int regno) const { return (uint8_t)(m_R[regno] & 0xff); } 103 | void SetLReg(int regno, uint8_t byte); 104 | uint16_t GetSP() const { return m_R[6]; } 105 | void SetSP(uint16_t word) { m_R[6] = word; } 106 | uint16_t GetPC() const { return m_R[7]; } 107 | uint16_t GetCPC() const { return m_savepc; } 108 | void SetPC(uint16_t word); 109 | void SetCPC(uint16_t word) { m_savepc = word; } 110 | 111 | public: // PSW bits control 112 | void SetC(bool bFlag); 113 | uint16_t GetC() const { return (m_psw & PSW_C) != 0; } 114 | void SetV(bool bFlag); 115 | uint16_t GetV() const { return (m_psw & PSW_V) != 0; } 116 | void SetN(bool bFlag); 117 | uint16_t GetN() const { return (m_psw & PSW_N) != 0; } 118 | void SetZ(bool bFlag); 119 | uint16_t GetZ() const { return (m_psw & PSW_Z) != 0; } 120 | void SetHALT(bool bFlag); 121 | uint16_t GetHALT() const { return (m_psw & PSW_HALT) != 0; } 122 | 123 | public: // Processor state 124 | /// \brief "Processor stopped" flag 125 | bool IsStopped() const { return m_okStopped; } 126 | /// \brief HALT flag (true - HALT mode, false - USER mode) 127 | bool IsHaltMode() const { return ((m_psw & 0400) != 0); } 128 | public: // Processor control 129 | void TickEVNT(); ///< EVNT signal 130 | /// \brief External interrupt via VIRQ signal 131 | void InterruptVIRQ(int que, uint16_t interrupt); 132 | uint16_t GetVIRQ(int que) { return m_virq[que]; } 133 | /// \brief Execute one processor tick 134 | void Execute(); 135 | /// \brief Process pending interrupt requests 136 | bool InterruptProcessing(); 137 | /// \brief Execute next command and process interrupts 138 | void CommandExecution(); 139 | int GetInternalTick() const { return m_internalTick; } 140 | void ClearInternalTick() { m_internalTick = 0; } 141 | void SetTrace(bool okTrace) { m_okTrace = okTrace; } ///< Set trace mode on/off 142 | 143 | public: // Saving/loading emulator status (pImage addresses up to 32 bytes) 144 | void SaveToImage(uint8_t* pImage) const; 145 | void LoadFromImage(const uint8_t* pImage); 146 | 147 | protected: // Implementation 148 | void FetchInstruction(); ///< Read next instruction 149 | void TranslateInstruction(); ///< Execute the instruction 150 | protected: // Implementation - memory access 151 | /// \brief Read word from the bus for execution 152 | uint16_t GetWordExec(uint16_t address) { return m_pMemoryController->GetWord(address, IsHaltMode(), true); } 153 | /// \brief Read word from the bus 154 | uint16_t GetWord(uint16_t address) { return m_pMemoryController->GetWord(address, IsHaltMode(), false); } 155 | void SetWord(uint16_t address, uint16_t word) { m_pMemoryController->SetWord(address, IsHaltMode(), word); } 156 | uint8_t GetByte(uint16_t address) { return m_pMemoryController->GetByte(address, IsHaltMode()); } 157 | void SetByte(uint16_t address, uint8_t byte) { m_pMemoryController->SetByte(address, IsHaltMode(), byte); } 158 | 159 | protected: // PSW bits calculations 160 | bool static CheckForNegative(uint8_t byte) { return (byte & 0200) != 0; } 161 | bool static CheckForNegative(uint16_t word) { return (word & 0100000) != 0; } 162 | bool static CheckForZero(uint8_t byte) { return byte == 0; } 163 | bool static CheckForZero(uint16_t word) { return word == 0; } 164 | bool static CheckAddForOverflow(uint8_t a, uint8_t b); 165 | bool static CheckAddForOverflow(uint16_t a, uint16_t b); 166 | bool static CheckSubForOverflow(uint8_t a, uint8_t b); 167 | bool static CheckSubForOverflow(uint16_t a, uint16_t b); 168 | bool static CheckAddForCarry(uint8_t a, uint8_t b); 169 | bool static CheckAddForCarry(uint16_t a, uint16_t b); 170 | bool static CheckSubForCarry(uint8_t a, uint8_t b); 171 | bool static CheckSubForCarry(uint16_t a, uint16_t b); 172 | 173 | protected: // Implementation - instruction execution 174 | // No fields 175 | uint16_t GetWordAddr (uint8_t meth, uint8_t reg); 176 | uint16_t GetByteAddr (uint8_t meth, uint8_t reg); 177 | 178 | void ExecuteUNKNOWN (); ///< There is no such instruction -- just call TRAP 10 179 | void ExecuteHALT (); 180 | void ExecuteWAIT (); 181 | void ExecuteRCPC(); 182 | void ExecuteRCPS (); 183 | void ExecuteWCPC(); 184 | void ExecuteWCPS(); 185 | void ExecuteMFUS (); 186 | void ExecuteMTUS (); 187 | void ExecuteRTI (); 188 | void ExecuteBPT (); 189 | void ExecuteIOT (); 190 | void ExecuteRESET (); 191 | void ExecuteSTEP(); 192 | void ExecuteRSEL (); 193 | void Execute000030 (); 194 | void ExecuteFIS (); 195 | void ExecuteRUN(); 196 | void ExecuteRTT (); 197 | void ExecuteCCC (); 198 | void ExecuteSCC (); 199 | 200 | // One fiels 201 | void ExecuteRTS (); 202 | 203 | // Two fields 204 | void ExecuteJMP (); 205 | void ExecuteSWAB (); 206 | void ExecuteCLR (); 207 | void ExecuteCLRB (); 208 | void ExecuteCOM (); 209 | void ExecuteCOMB (); 210 | void ExecuteINC (); 211 | void ExecuteINCB (); 212 | void ExecuteDEC (); 213 | void ExecuteDECB (); 214 | void ExecuteNEG (); 215 | void ExecuteNEGB (); 216 | void ExecuteADC (); 217 | void ExecuteADCB (); 218 | void ExecuteSBC (); 219 | void ExecuteSBCB (); 220 | void ExecuteTST (); 221 | void ExecuteTSTB (); 222 | void ExecuteROR (); 223 | void ExecuteRORB (); 224 | void ExecuteROL (); 225 | void ExecuteROLB (); 226 | void ExecuteASR (); 227 | void ExecuteASRB (); 228 | void ExecuteASL (); 229 | void ExecuteASLB (); 230 | void ExecuteMARK (); 231 | void ExecuteSXT (); 232 | void ExecuteMTPS (); 233 | void ExecuteMFPS (); 234 | 235 | // Branchs & interrupts 236 | void ExecuteBR (); 237 | void ExecuteBNE (); 238 | void ExecuteBEQ (); 239 | void ExecuteBGE (); 240 | void ExecuteBLT (); 241 | void ExecuteBGT (); 242 | void ExecuteBLE (); 243 | void ExecuteBPL (); 244 | void ExecuteBMI (); 245 | void ExecuteBHI (); 246 | void ExecuteBLOS (); 247 | void ExecuteBVC (); 248 | void ExecuteBVS (); 249 | void ExecuteBHIS (); 250 | void ExecuteBLO (); 251 | 252 | void ExecuteEMT (); 253 | void ExecuteTRAP (); 254 | 255 | // Three fields 256 | void ExecuteJSR (); 257 | void ExecuteXOR (); 258 | void ExecuteSOB (); 259 | void ExecuteMUL (); 260 | void ExecuteDIV (); 261 | void ExecuteASH (); 262 | void ExecuteASHC (); 263 | 264 | // Four fields 265 | void ExecuteMOV (); 266 | void ExecuteMOVB (); 267 | void ExecuteCMP (); 268 | void ExecuteCMPB (); 269 | void ExecuteBIT (); 270 | void ExecuteBITB (); 271 | void ExecuteBIC (); 272 | void ExecuteBICB (); 273 | void ExecuteBIS (); 274 | void ExecuteBISB (); 275 | 276 | void ExecuteADD (); 277 | void ExecuteSUB (); 278 | }; 279 | 280 | inline void CProcessor::SetPSW(uint16_t word) 281 | { 282 | m_psw = word & 0777; 283 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 284 | } 285 | inline void CProcessor::SetLPSW(uint8_t byte) 286 | { 287 | m_psw = (m_psw & 0xFF00) | (uint16_t)byte; 288 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 289 | } 290 | inline void CProcessor::SetReg(int regno, uint16_t word) 291 | { 292 | m_R[regno] = word; 293 | if ((regno == 7) && ((m_psw & 0600) != 0600)) m_savepc = word; 294 | } 295 | inline void CProcessor::SetLReg(int regno, uint8_t byte) 296 | { 297 | m_R[regno] = (m_R[regno] & 0xFF00) | (uint16_t)byte; 298 | if ((regno == 7) && ((m_psw & 0600) != 0600)) m_savepc = m_R[7]; 299 | } 300 | inline void CProcessor::SetPC(uint16_t word) 301 | { 302 | m_R[7] = word; 303 | if ((m_psw & 0600) != 0600) m_savepc = word; 304 | } 305 | 306 | // PSW bits control - implementation 307 | inline void CProcessor::SetC (bool bFlag) 308 | { 309 | if (bFlag) m_psw |= PSW_C; else m_psw &= ~PSW_C; 310 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 311 | } 312 | inline void CProcessor::SetV (bool bFlag) 313 | { 314 | if (bFlag) m_psw |= PSW_V; else m_psw &= ~PSW_V; 315 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 316 | } 317 | inline void CProcessor::SetN (bool bFlag) 318 | { 319 | if (bFlag) m_psw |= PSW_N; else m_psw &= ~PSW_N; 320 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 321 | } 322 | inline void CProcessor::SetZ (bool bFlag) 323 | { 324 | if (bFlag) m_psw |= PSW_Z; else m_psw &= ~PSW_Z; 325 | if ((m_psw & 0600) != 0600) m_savepsw = m_psw; 326 | } 327 | 328 | inline void CProcessor::SetHALT (bool bFlag) 329 | { 330 | if (bFlag) m_psw |= PSW_HALT; else m_psw &= ~PSW_HALT; 331 | } 332 | 333 | inline void CProcessor::InterruptVIRQ(int que, uint16_t interrupt) 334 | { 335 | if (m_okStopped) return; // Processor is stopped - nothing to do 336 | m_virq[que] = interrupt; 337 | } 338 | 339 | // PSW bits calculations - implementation 340 | inline bool CProcessor::CheckAddForOverflow (uint8_t a, uint8_t b) 341 | { 342 | #if defined(_M_IX86) && defined(_MSC_VER) && !defined(_MANAGED) 343 | bool bOverflow = false; 344 | _asm 345 | { 346 | pushf 347 | push cx 348 | mov cl, byte ptr [a] 349 | add cl, byte ptr [b] 350 | jno end 351 | mov dword ptr [bOverflow], 1 352 | end: 353 | pop cx 354 | popf 355 | } 356 | return bOverflow; 357 | #else 358 | //uint16_t sum = a < 0200 ? (uint16_t)a + (uint16_t)b + 0200 : (uint16_t)a + (uint16_t)b - 0200; 359 | //return HIBYTE (sum) != 0; 360 | uint8_t sum = a + b; 361 | return ((~a ^ b) & (a ^ sum)) & 0200; 362 | #endif 363 | } 364 | inline bool CProcessor::CheckAddForOverflow (uint16_t a, uint16_t b) 365 | { 366 | #if defined(_M_IX86) && defined(_MSC_VER) && !defined(_MANAGED) 367 | bool bOverflow = false; 368 | _asm 369 | { 370 | pushf 371 | push cx 372 | mov cx, word ptr [a] 373 | add cx, word ptr [b] 374 | jno end 375 | mov dword ptr [bOverflow], 1 376 | end: 377 | pop cx 378 | popf 379 | } 380 | return bOverflow; 381 | #else 382 | //uint32_t sum = a < 0100000 ? (uint32_t)a + (uint32_t)b + 0100000 : (uint32_t)a + (uint32_t)b - 0100000; 383 | //return HIWORD (sum) != 0; 384 | uint16_t sum = a + b; 385 | return ((~a ^ b) & (a ^ sum)) & 0100000; 386 | #endif 387 | } 388 | 389 | inline bool CProcessor::CheckSubForOverflow (uint8_t a, uint8_t b) 390 | { 391 | #if defined(_M_IX86) && defined(_MSC_VER) && !defined(_MANAGED) 392 | bool bOverflow = false; 393 | _asm 394 | { 395 | pushf 396 | push cx 397 | mov cl, byte ptr [a] 398 | sub cl, byte ptr [b] 399 | jno end 400 | mov dword ptr [bOverflow], 1 401 | end: 402 | pop cx 403 | popf 404 | } 405 | return bOverflow; 406 | #else 407 | //uint16_t sum = a < 0200 ? (uint16_t)a - (uint16_t)b + 0200 : (uint16_t)a - (uint16_t)b - 0200; 408 | //return HIBYTE (sum) != 0; 409 | uint8_t sum = a - b; 410 | return ((a ^ b) & (~b ^ sum)) & 0200; 411 | #endif 412 | } 413 | inline bool CProcessor::CheckSubForOverflow (uint16_t a, uint16_t b) 414 | { 415 | #if defined(_M_IX86) && defined(_MSC_VER) && !defined(_MANAGED) 416 | bool bOverflow = false; 417 | _asm 418 | { 419 | pushf 420 | push cx 421 | mov cx, word ptr [a] 422 | sub cx, word ptr [b] 423 | jno end 424 | mov dword ptr [bOverflow], 1 425 | end: 426 | pop cx 427 | popf 428 | } 429 | return bOverflow; 430 | #else 431 | //uint32_t sum = a < 0100000 ? (uint32_t)a - (uint32_t)b + 0100000 : (uint32_t)a - (uint32_t)b - 0100000; 432 | //return HIWORD (sum) != 0; 433 | uint16_t sum = a - b; 434 | return ((a ^ b) & (~b ^ sum)) & 0100000; 435 | #endif 436 | } 437 | inline bool CProcessor::CheckAddForCarry (uint8_t a, uint8_t b) 438 | { 439 | uint16_t sum = (uint16_t)a + (uint16_t)b; 440 | return (uint8_t)((sum >> 8) & 0xff) != 0; 441 | } 442 | inline bool CProcessor::CheckAddForCarry (uint16_t a, uint16_t b) 443 | { 444 | uint32_t sum = (uint32_t)a + (uint32_t)b; 445 | return (uint16_t)((sum >> 16) & 0xffff) != 0; 446 | } 447 | inline bool CProcessor::CheckSubForCarry (uint8_t a, uint8_t b) 448 | { 449 | uint16_t sum = (uint16_t)a - (uint16_t)b; 450 | return (uint8_t)((sum >> 8) & 0xff) != 0; 451 | } 452 | inline bool CProcessor::CheckSubForCarry (uint16_t a, uint16_t b) 453 | { 454 | uint32_t sum = (uint32_t)a - (uint32_t)b; 455 | return (uint16_t)((sum >> 16) & 0xffff) != 0; 456 | } 457 | 458 | 459 | ////////////////////////////////////////////////////////////////////// 460 | -------------------------------------------------------------------------------- /emubase/Floppy.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | /// \file Floppy.cpp 12 | /// \brief Floppy controller and drives emulation 13 | /// \details See defines in header file Emubase.h 14 | 15 | #include "stdafx.h" 16 | #include 17 | #include "Emubase.h" 18 | 19 | 20 | ////////////////////////////////////////////////////////////////////// 21 | 22 | // Маска флагов, сохраняемых в m_flags 23 | const uint16_t FLOPPY_CMD_MASKSTORED = 24 | FLOPPY_CMD_CORRECTION250 | FLOPPY_CMD_CORRECTION500 | FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_SKIPSYNC | 25 | FLOPPY_CMD_ENGINESTART; 26 | 27 | static void EncodeTrackData(const uint8_t* pSrc, uint8_t* data, uint8_t* marker, uint16_t track, uint16_t side); 28 | static bool DecodeTrackData(const uint8_t* pRaw, uint8_t* pDest); 29 | 30 | ////////////////////////////////////////////////////////////////////// 31 | 32 | 33 | CFloppyDrive::CFloppyDrive() 34 | { 35 | fpFile = nullptr; 36 | okNetRT11Image = okReadOnly = false; 37 | datatrack = dataside = 0; 38 | dataptr = 0; 39 | track = side = 0; 40 | memset(data, 0, FLOPPY_RAWTRACKSIZE); 41 | memset(marker, 0, FLOPPY_RAWMARKERSIZE); 42 | } 43 | 44 | void CFloppyDrive::Reset() 45 | { 46 | datatrack = dataside = 0; 47 | dataptr = 0; 48 | } 49 | 50 | 51 | ////////////////////////////////////////////////////////////////////// 52 | 53 | 54 | CFloppyController::CFloppyController() 55 | { 56 | m_drive = m_side = m_track = 0; 57 | m_pDrive = m_drivedata; 58 | m_datareg = m_writereg = m_shiftreg = 0; 59 | m_writing = m_searchsync = m_writemarker = m_crccalculus = false; 60 | m_writeflag = m_shiftflag = m_shiftmarker = false; 61 | m_trackchanged = false; 62 | m_status = FLOPPY_STATUS_TRACK0 | FLOPPY_STATUS_WRITEPROTECT; 63 | m_flags = FLOPPY_CMD_CORRECTION500 | FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_SKIPSYNC; 64 | m_okTrace = 0; 65 | } 66 | 67 | CFloppyController::~CFloppyController() 68 | { 69 | for (int drive = 0; drive < 4; drive++) 70 | DetachImage(drive); 71 | } 72 | 73 | void CFloppyController::Reset() 74 | { 75 | if (m_okTrace) DebugLog(_T("Floppy RESET\r\n")); 76 | 77 | FlushChanges(); 78 | 79 | m_drive = m_side = m_track = 0; 80 | m_pDrive = m_drivedata; 81 | m_datareg = m_writereg = m_shiftreg = 0; 82 | m_writing = m_searchsync = m_writemarker = m_crccalculus = false; 83 | m_writeflag = m_shiftflag = false; 84 | m_trackchanged = false; 85 | m_status = (m_pDrive->okReadOnly) ? FLOPPY_STATUS_TRACK0 | FLOPPY_STATUS_WRITEPROTECT : FLOPPY_STATUS_TRACK0; 86 | m_flags = FLOPPY_CMD_CORRECTION500 | FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_SKIPSYNC; 87 | 88 | PrepareTrack(); 89 | } 90 | 91 | bool CFloppyController::AttachImage(int drive, LPCTSTR sFileName) 92 | { 93 | ASSERT(sFileName != nullptr); 94 | 95 | // If image attached - detach one first 96 | if (m_drivedata[drive].fpFile != nullptr) 97 | DetachImage(drive); 98 | 99 | // Определяем, это .dsk-образ или .rtd-образ - по расширению файла 100 | m_drivedata[drive].okNetRT11Image = false; 101 | LPCTSTR sFileNameExt = _tcsrchr(sFileName, _T('.')); 102 | if (sFileNameExt != nullptr && _tcsicmp(sFileNameExt, _T(".rtd")) == 0) 103 | m_drivedata[drive].okNetRT11Image = true; 104 | 105 | // Open file 106 | m_drivedata[drive].okReadOnly = false; 107 | m_drivedata[drive].fpFile = ::_tfopen(sFileName, _T("r+b")); 108 | if (m_drivedata[drive].fpFile == nullptr) 109 | { 110 | m_drivedata[drive].okReadOnly = true; 111 | m_drivedata[drive].fpFile = ::_tfopen(sFileName, _T("rb")); 112 | } 113 | if (m_drivedata[drive].fpFile == nullptr) 114 | return false; 115 | 116 | m_side = m_track = m_drivedata[drive].datatrack = m_drivedata[drive].dataside = 0; 117 | m_drivedata[drive].dataptr = 0; 118 | m_datareg = m_writereg = m_shiftreg = 0; 119 | m_writing = m_searchsync = m_writemarker = m_crccalculus = false; 120 | m_writeflag = m_shiftflag = false; 121 | m_trackchanged = false; 122 | m_status = (m_pDrive->okReadOnly) ? FLOPPY_STATUS_TRACK0 | FLOPPY_STATUS_WRITEPROTECT : FLOPPY_STATUS_TRACK0; 123 | m_flags = FLOPPY_CMD_CORRECTION500 | FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_SKIPSYNC; 124 | 125 | PrepareTrack(); 126 | 127 | return true; 128 | } 129 | 130 | void CFloppyController::DetachImage(int drive) 131 | { 132 | if (m_drivedata[drive].fpFile == nullptr) return; 133 | 134 | FlushChanges(); 135 | 136 | ::fclose(m_drivedata[drive].fpFile); 137 | m_drivedata[drive].fpFile = nullptr; 138 | m_drivedata[drive].okNetRT11Image = m_drivedata[drive].okReadOnly = false; 139 | m_drivedata[drive].Reset(); 140 | } 141 | 142 | ////////////////////////////////////////////////////////////////////// 143 | 144 | 145 | uint16_t CFloppyController::GetState(void) 146 | { 147 | if (m_track == 0) 148 | m_status |= FLOPPY_STATUS_TRACK0; 149 | else 150 | m_status &= ~FLOPPY_STATUS_TRACK0; 151 | if (m_pDrive->dataptr < FLOPPY_INDEXLENGTH) 152 | m_status |= FLOPPY_STATUS_INDEXMARK; 153 | else 154 | m_status &= ~FLOPPY_STATUS_INDEXMARK; 155 | 156 | uint16_t res = m_status; 157 | 158 | if (m_drivedata[m_drive].fpFile == nullptr) 159 | res |= FLOPPY_STATUS_MOREDATA; 160 | 161 | // if (res & FLOPPY_STATUS_MOREDATA) 162 | // { 163 | // TCHAR oct2[7]; PrintOctalValue(oct2, res); 164 | // DebugLogFormat(_T("Floppy GET STATE %s\r\n"), oct2); 165 | // } 166 | 167 | return res; 168 | } 169 | 170 | void CFloppyController::SetCommand(uint16_t cmd) 171 | { 172 | if (m_okTrace) DebugLogFormat(_T("Floppy COMMAND %06o\r\n"), cmd); 173 | 174 | bool okPrepareTrack = false; // Нужно ли считывать дорожку в буфер 175 | 176 | // Проверить, не сменился ли текущий привод; bit 10 (REZ) should be set 177 | uint16_t newdrive = (cmd & 3) ^ 3; 178 | if ((cmd & 02000) != 0 && m_drive != newdrive) 179 | { 180 | FlushChanges(); 181 | 182 | DebugLogFormat(_T("Floppy DRIVE %hu\r\n"), newdrive); 183 | 184 | m_drive = newdrive; 185 | m_pDrive = m_drivedata + m_drive; 186 | okPrepareTrack = true; 187 | } 188 | cmd &= ~3; // Убираем из команды информацию о текущем приводе 189 | 190 | // Copy new flags to m_flags 191 | m_flags &= ~FLOPPY_CMD_MASKSTORED; 192 | m_flags |= cmd & FLOPPY_CMD_MASKSTORED; 193 | 194 | // Проверяем, не сменилась ли сторона 195 | if (m_flags & FLOPPY_CMD_SIDEUP) // Side selection: 0 - down, 1 - up 196 | { 197 | if (m_side == 0) { m_side = 1; okPrepareTrack = true; } 198 | } 199 | else 200 | { 201 | if (m_side == 1) { m_side = 0; okPrepareTrack = true; } 202 | } 203 | 204 | if (cmd & FLOPPY_CMD_STEP) // Move head for one track to center or from center 205 | { 206 | if (m_okTrace) DebugLog(_T("Floppy STEP\r\n")); 207 | 208 | m_side = (m_flags & FLOPPY_CMD_SIDEUP) ? 1 : 0; // DO WE NEED IT HERE? 209 | 210 | if (m_flags & FLOPPY_CMD_DIR) 211 | { 212 | if (m_track < 79) { m_track++; okPrepareTrack = true; } 213 | } 214 | else 215 | { 216 | if (m_track >= 1) { m_track--; okPrepareTrack = true; } 217 | } 218 | } 219 | if (okPrepareTrack) 220 | { 221 | PrepareTrack(); 222 | 223 | // DebugLogFormat(_T("Floppy DRIVE %hu TR %hu SD %hu\r\n"), m_drive, m_track, m_side); 224 | } 225 | 226 | if (cmd & FLOPPY_CMD_SEARCHSYNC) // Search for marker 227 | { 228 | // DebugLog(_T("Floppy SEARCHSYNC\r\n")); 229 | 230 | m_flags &= ~FLOPPY_CMD_SEARCHSYNC; 231 | m_searchsync = true; 232 | m_crccalculus = true; 233 | m_status &= ~FLOPPY_STATUS_CHECKSUMOK; 234 | } 235 | 236 | if (m_writing && (cmd & FLOPPY_CMD_SKIPSYNC)) // Запись маркера 237 | { 238 | // DebugLog(_T("Floppy MARKER\r\n")); 239 | 240 | m_writemarker = true; 241 | m_status &= ~FLOPPY_STATUS_CHECKSUMOK; 242 | } 243 | } 244 | 245 | uint16_t CFloppyController::GetData(void) 246 | { 247 | if (m_okTrace) DebugLogFormat(_T("Floppy READ\t\t%04x\r\n"), m_datareg); 248 | 249 | m_status &= ~FLOPPY_STATUS_MOREDATA; 250 | m_writing = m_searchsync = false; 251 | m_writeflag = m_shiftflag = false; 252 | 253 | return m_datareg; 254 | } 255 | 256 | void CFloppyController::WriteData(uint16_t data) 257 | { 258 | // DebugLogFormat(_T("Floppy WRITE\t\t%04x\r\n"), data); 259 | 260 | m_writing = true; // Switch to write mode if not yet 261 | m_searchsync = false; 262 | 263 | if (!m_writeflag && !m_shiftflag) // Both registers are empty 264 | { 265 | m_shiftreg = data; 266 | m_shiftflag = true; 267 | m_status |= FLOPPY_STATUS_MOREDATA; 268 | } 269 | else if (!m_writeflag && m_shiftflag) // Write register is empty 270 | { 271 | m_writereg = data; 272 | m_writeflag = true; 273 | m_status &= ~FLOPPY_STATUS_MOREDATA; 274 | } 275 | else if (m_writeflag && !m_shiftflag) // Shift register is empty 276 | { 277 | m_shiftreg = m_writereg; 278 | m_shiftflag = m_writeflag; 279 | m_writereg = data; 280 | m_writeflag = true; 281 | m_status &= ~FLOPPY_STATUS_MOREDATA; 282 | } 283 | else // Both registers are not empty 284 | { 285 | m_writereg = data; 286 | } 287 | } 288 | 289 | void CFloppyController::Periodic() 290 | { 291 | //if (!IsEngineOn()) return; // Вращаем дискеты только если включен мотор 292 | 293 | // Вращаем дискеты во всех драйвах сразу 294 | for (int drive = 0; drive < 4; drive++) 295 | { 296 | m_drivedata[drive].dataptr += 2; 297 | if (m_drivedata[drive].dataptr >= FLOPPY_RAWTRACKSIZE) 298 | m_drivedata[drive].dataptr = 0; 299 | } 300 | 301 | // Далее обрабатываем чтение/запись на текущем драйве 302 | if (!IsAttached(m_drive)) return; 303 | 304 | if (!m_writing) // Read mode 305 | { 306 | m_datareg = (m_pDrive->data[m_pDrive->dataptr] << 8) | m_pDrive->data[m_pDrive->dataptr + 1]; 307 | if (m_status & FLOPPY_STATUS_MOREDATA) 308 | { 309 | if (m_crccalculus) // Stop CRC calculation 310 | { 311 | m_crccalculus = false; 312 | //TODO: Compare calculated CRC to m_datareg 313 | m_status |= FLOPPY_STATUS_CHECKSUMOK; 314 | } 315 | } 316 | else 317 | { 318 | if (m_searchsync) // Search for marker 319 | { 320 | if (m_pDrive->marker[m_pDrive->dataptr / 2]) // Marker found 321 | { 322 | m_status |= FLOPPY_STATUS_MOREDATA; 323 | m_searchsync = false; 324 | } 325 | } 326 | else // Just read 327 | m_status |= FLOPPY_STATUS_MOREDATA; 328 | } 329 | } 330 | else // Write mode 331 | { 332 | if (m_shiftflag) 333 | { 334 | m_pDrive->data[m_pDrive->dataptr] = (uint8_t)(m_shiftreg & 0xff); 335 | m_pDrive->data[m_pDrive->dataptr + 1] = (uint8_t)((m_shiftreg >> 8) & 0xff); 336 | m_shiftflag = false; 337 | m_trackchanged = true; 338 | 339 | if (m_shiftmarker) 340 | { 341 | // DebugLogFormat(_T("Floppy WRITING %04x MARKER at %04hx SC %hu\r\n"), m_shiftreg, m_pDrive->dataptr, (m_pDrive->dataptr - 0x5e) / 614 + 1); 342 | 343 | m_pDrive->marker[m_pDrive->dataptr / 2] = true; 344 | m_shiftmarker = false; 345 | m_crccalculus = true; // Start CRC calculation 346 | } 347 | else 348 | { 349 | // DebugLogFormat(_T("Floppy WRITING %04x\r\n"), m_shiftreg); 350 | 351 | m_pDrive->marker[m_pDrive->dataptr / 2] = false; 352 | } 353 | 354 | if (m_writeflag) 355 | { 356 | m_shiftreg = m_writereg; 357 | m_shiftflag = m_writeflag; m_writeflag = false; 358 | m_shiftmarker = m_writemarker; m_writemarker = false; 359 | m_status |= FLOPPY_STATUS_MOREDATA; 360 | } 361 | else 362 | { 363 | if (m_crccalculus) // Stop CRC calclation 364 | { 365 | m_shiftreg = 0x4444; //STUB 366 | m_shiftflag = false; //Should be true, but temporarily disabled 367 | m_shiftmarker = false; 368 | m_crccalculus = false; 369 | m_status |= FLOPPY_STATUS_CHECKSUMOK; 370 | } 371 | } 372 | } 373 | } 374 | } 375 | 376 | // Read track data from file and fill m_data 377 | void CFloppyController::PrepareTrack() 378 | { 379 | FlushChanges(); 380 | 381 | if (m_okTrace) DebugLogFormat(_T("Floppy PREP %hu TR %hu SD %hu\r\n"), m_drive, m_track, m_side); 382 | 383 | //TCHAR buffer[512]; 384 | 385 | m_trackchanged = false; 386 | m_status |= FLOPPY_STATUS_MOREDATA; 387 | //NOTE: Not changing m_pDrive->dataptr 388 | m_pDrive->datatrack = m_track; 389 | m_pDrive->dataside = m_side; 390 | 391 | // Track has 10 sectors, 512 bytes each; offset of the file is === ((Track<<1)+SIDE)*5120 392 | long foffset = ((m_track * 2) + (m_side)) * 5120; 393 | if (m_pDrive->okNetRT11Image) foffset += 256; // Skip .RTD image header 394 | //DebugPrintFormat(_T("floppy file offset %d for trk %d side %d\r\n"), foffset, m_track, m_side); 395 | 396 | uint8_t data[5120]; 397 | memset(data, 0, 5120); 398 | 399 | if (m_pDrive->fpFile != nullptr) 400 | { 401 | ::fseek(m_pDrive->fpFile, foffset, SEEK_SET); 402 | size_t count = ::fread(data, 1, 5120, m_pDrive->fpFile); 403 | //TODO: Контроль ошибок чтения 404 | } 405 | 406 | // Fill m_data array and m_marker array with marked data 407 | EncodeTrackData(data, m_pDrive->data, m_pDrive->marker, m_track, m_side); 408 | 409 | ////DEBUG: Test DecodeTrackData() 410 | //uint8_t data2[5120]; 411 | //bool parsed = DecodeTrackData(m_pDrive->data, data2); 412 | //ASSERT(parsed); 413 | //bool tested = true; 414 | //for (int i = 0; i < 5120; i++) 415 | // if (data[i] != data2[i]) 416 | // { 417 | // tested = false; 418 | // break; 419 | // } 420 | //ASSERT(tested); 421 | } 422 | 423 | void CFloppyController::FlushChanges() 424 | { 425 | if (!IsAttached(m_drive)) return; 426 | if (!m_trackchanged) return; 427 | 428 | if (m_okTrace) DebugLogFormat(_T("Floppy FLUSH %hu TR %hu SD %hu\r\n"), m_drive, m_pDrive->datatrack, m_pDrive->dataside); 429 | 430 | // Decode track data from m_data 431 | uint8_t data[5120]; memset(data, 0, 5120); 432 | bool decoded = DecodeTrackData(m_pDrive->data, data); 433 | 434 | if (decoded) // Write to the file only if the track was correctly decoded from raw data 435 | { 436 | // Track has 10 sectors, 512 bytes each; offset of the file is === ((Track<<1)+SIDE)*5120 437 | long foffset = ((m_pDrive->datatrack * 2) + (m_pDrive->dataside)) * 5120; 438 | if (m_pDrive->okNetRT11Image) foffset += 256; // Skip .RTD image header 439 | 440 | // Check file length 441 | ::fseek(m_pDrive->fpFile, 0, SEEK_END); 442 | uint32_t currentFileSize = ::ftell(m_pDrive->fpFile); 443 | while (currentFileSize < (uint32_t)(foffset + 5120)) 444 | { 445 | uint8_t datafill[512]; ::memset(datafill, 0, 512); 446 | uint32_t bytesToWrite = ((uint32_t)(foffset + 5120) - currentFileSize) % 512; 447 | if (bytesToWrite == 0) bytesToWrite = 512; 448 | ::fwrite(datafill, 1, bytesToWrite, m_pDrive->fpFile); 449 | //TODO: Проверка на ошибки записи 450 | currentFileSize += bytesToWrite; 451 | } 452 | 453 | // Save data into the file 454 | ::fseek(m_pDrive->fpFile, foffset, SEEK_SET); 455 | size_t dwBytesWritten = ::fwrite(data, 1, 5120, m_pDrive->fpFile); 456 | //TODO: Проверка на ошибки записи 457 | } 458 | else 459 | { 460 | if (m_okTrace) DebugLog(_T("Floppy FLUSH FAILED\r\n")); 461 | } 462 | 463 | m_trackchanged = false; 464 | 465 | ////DEBUG: Save raw m_data/m_marker into rawdata.bin 466 | //HANDLE hRawFile = CreateFile(_T("rawdata.bin"), 467 | // GENERIC_WRITE, FILE_SHARE_READ, nullptr, 468 | // CREATE_ALWAYS, 0, nullptr); 469 | } 470 | 471 | 472 | ////////////////////////////////////////////////////////////////////// 473 | 474 | 475 | // Fill data array and marker array with marked data 476 | static void EncodeTrackData(const uint8_t* pSrc, uint8_t* data, uint8_t* marker, uint16_t track, uint16_t side) 477 | { 478 | memset(data, 0, FLOPPY_RAWTRACKSIZE); 479 | memset(marker, 0, FLOPPY_RAWMARKERSIZE); 480 | uint32_t count; 481 | int ptr = 0; 482 | int gap = 34; 483 | for (int sect = 0; sect < 10; sect++) 484 | { 485 | // GAP 486 | for (count = 0; count < (uint32_t) gap; count++) data[ptr++] = 0x4e; 487 | // sector header 488 | for (count = 0; count < 12; count++) data[ptr++] = 0x00; 489 | // marker 490 | marker[ptr / 2] = true; // ID marker; start CRC calculus 491 | data[ptr++] = 0xa1; data[ptr++] = 0xa1; data[ptr++] = 0xa1; 492 | data[ptr++] = 0xfe; 493 | 494 | data[ptr++] = (uint8_t) track; data[ptr++] = (side != 0); 495 | data[ptr++] = (uint8_t)sect + 1; data[ptr++] = 2; // Assume 512 bytes per sector; 496 | // crc 497 | //TODO: Calculate CRC 498 | data[ptr++] = 0x12; data[ptr++] = 0x34; // CRC stub 499 | 500 | // sync 501 | for (count = 0; count < 24; count++) data[ptr++] = 0x4e; 502 | // data header 503 | for (count = 0; count < 12; count++) data[ptr++] = 0x00; 504 | // marker 505 | marker[ptr / 2] = true; // Data marker; start CRC calculus 506 | data[ptr++] = 0xa1; data[ptr++] = 0xa1; data[ptr++] = 0xa1; 507 | data[ptr++] = 0xfb; 508 | // data 509 | for (count = 0; count < 512; count++) 510 | data[ptr++] = pSrc[(sect * 512) + count]; 511 | // crc 512 | //TODO: Calculate CRC 513 | data[ptr++] = 0x43; data[ptr++] = 0x21; // CRC stub 514 | 515 | gap = 38; 516 | } 517 | // fill GAP4B to the end of the track 518 | while (ptr < FLOPPY_RAWTRACKSIZE) data[ptr++] = 0x4e; 519 | } 520 | 521 | // Decode track data from raw data 522 | // pRaw is array of FLOPPY_RAWTRACKSIZE bytes 523 | // pDest is array of 5120 bytes 524 | // Returns: true - decoded, false - parse error 525 | static bool DecodeTrackData(const uint8_t* pRaw, uint8_t* pDest) 526 | { 527 | uint16_t dataptr = 0; // Offset in m_data array 528 | uint16_t destptr = 0; // Offset in data array 529 | for (;;) 530 | { 531 | while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x4e) dataptr++; // Skip GAP1 or GAP3 532 | if (dataptr >= FLOPPY_RAWTRACKSIZE) break; // End of track or error 533 | while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x00) dataptr++; // Skip sync 534 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 535 | 536 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 537 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 538 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 539 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 540 | if (pRaw[dataptr++] != 0xfe) 541 | return false; // Marker not found 542 | 543 | uint8_t sectcyl, secthd, sectsec, sectno = 0; 544 | if (dataptr < FLOPPY_RAWTRACKSIZE) sectcyl = pRaw[dataptr++]; 545 | if (dataptr < FLOPPY_RAWTRACKSIZE) secthd = pRaw[dataptr++]; 546 | if (dataptr < FLOPPY_RAWTRACKSIZE) sectsec = pRaw[dataptr++]; 547 | if (dataptr < FLOPPY_RAWTRACKSIZE) sectno = pRaw[dataptr++]; 548 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 549 | 550 | int sectorsize; 551 | if (sectno == 1) sectorsize = 256; 552 | else if (sectno == 2) sectorsize = 512; 553 | else if (sectno == 3) sectorsize = 1024; 554 | else return false; // Something wrong: unknown sector size 555 | // crc 556 | if (dataptr < FLOPPY_RAWTRACKSIZE) dataptr++; 557 | if (dataptr < FLOPPY_RAWTRACKSIZE) dataptr++; 558 | 559 | while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x4e) dataptr++; // Skip GAP2 560 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 561 | while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x00) dataptr++; // Skip sync 562 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 563 | 564 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 565 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 566 | if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1) dataptr++; 567 | if (dataptr >= FLOPPY_RAWTRACKSIZE) return false; // Something wrong 568 | if (pRaw[dataptr++] != 0xfb) 569 | return false; // Marker not found 570 | 571 | for (int count = 0; count < sectorsize; count++) // Copy sector data 572 | { 573 | if (destptr >= 5120) break; // End of track or error 574 | pDest[destptr++] = pRaw[dataptr++]; 575 | if (dataptr >= FLOPPY_RAWTRACKSIZE) 576 | return false; // Something wrong 577 | } 578 | if (dataptr >= FLOPPY_RAWTRACKSIZE) 579 | return false; // Something wrong 580 | // crc 581 | if (dataptr < FLOPPY_RAWTRACKSIZE) dataptr++; 582 | if (dataptr < FLOPPY_RAWTRACKSIZE) dataptr++; 583 | } 584 | 585 | return true; 586 | } 587 | 588 | 589 | ////////////////////////////////////////////////////////////////////// 590 | 591 | -------------------------------------------------------------------------------- /Emulator.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of UKNCBTL. 2 | UKNCBTL is free software: you can redistribute it and/or modify it under the terms 3 | of the GNU Lesser General Public License as published by the Free Software Foundation, 4 | either version 3 of the License, or (at your option) any later version. 5 | UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 6 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | See the GNU Lesser General Public License for more details. 8 | You should have received a copy of the GNU Lesser General Public License along with 9 | UKNCBTL. If not, see . */ 10 | 11 | // Emulator.cpp 12 | 13 | #include "stdafx.h" 14 | #include "emubase\Emubase.h" 15 | #include 16 | #include "miniz/zip.h" 17 | 18 | 19 | // DebugPrint and DebugLog 20 | void DebugPrint(LPCTSTR) {} 21 | void DebugPrintFormat(LPCTSTR, ...) {} 22 | void DebugLogClear() {} 23 | void DebugLogCloseFile() {} 24 | void DebugLog(LPCTSTR) {} 25 | void DebugLogFormat(LPCTSTR, ...) {} 26 | 27 | 28 | enum ScreenViewMode 29 | { 30 | RGBScreen = 1, 31 | GrayScreen = 2, 32 | GRBScreen = 3, 33 | }; 34 | 35 | void Emulator_PrepareScreenRGB32(void* pBits, const uint32_t* colors); 36 | 37 | 38 | const uint32_t Emulator_CanvasRGBAColors[16 * 8] = 39 | { 40 | 0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000, 0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080, 41 | 0xFF000000, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00, 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF, 42 | 0xFF000000, 0xFF600000, 0xFF008000, 0xFF608000, 0xFF000080, 0xFF600080, 0xFF008080, 0xFF608080, 43 | 0xFF000000, 0xFFDF0000, 0xFF00FF00, 0xFFDFFF00, 0xFF0000FF, 0xFFDF00FF, 0xFF00FFFF, 0xFFDFFFFF, 44 | 0xFF000000, 0xFF800000, 0xFF006000, 0xFF806000, 0xFF000080, 0xFF800080, 0xFF006080, 0xFF806080, 45 | 0xFF000000, 0xFFFF0000, 0xFF00DF00, 0xFFFFDF00, 0xFF0000FF, 0xFFFF00FF, 0xFF00DFFF, 0xFFFFDFFF, 46 | 0xFF000000, 0xFF600000, 0xFF006000, 0xFF606000, 0xFF000080, 0xFF600080, 0xFF006080, 0xFF606080, 47 | 0xFF000000, 0xFFDF0000, 0xFF00DF00, 0xFFDFDF00, 0xFF0000FF, 0xFFDF00FF, 0xFF00DFFF, 0xFFDFDFFF, 48 | 0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000, 0xFF000060, 0xFF800060, 0xFF008060, 0xFF808060, 49 | 0xFF000000, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00, 0xFF0000DF, 0xFFFF00DF, 0xFF00FFDF, 0xFFFFFFDF, 50 | 0xFF000000, 0xFF600000, 0xFF008000, 0xFF608000, 0xFF000060, 0xFF600060, 0xFF008060, 0xFF608060, 51 | 0xFF000000, 0xFFDF0000, 0xFF00FF00, 0xFFDFFF00, 0xFF0000DF, 0xFFDF00DF, 0xFF00FFDF, 0xFFDFFFDF, 52 | 0xFF000000, 0xFF800000, 0xFF006000, 0xFF806000, 0xFF000060, 0xFF800060, 0xFF006060, 0xFF806060, 53 | 0xFF000000, 0xFFFF0000, 0xFF00DF00, 0xFFFFDF00, 0xFF0000DF, 0xFFFF00DF, 0xFF00DFDF, 0xFFFFDFDF, 54 | 0xFF000000, 0xFF600000, 0xFF006000, 0xFF606000, 0xFF000060, 0xFF600060, 0xFF006060, 0xFF606060, 55 | 0xFF000000, 0xFFDF0000, 0xFF00DF00, 0xFFDFDF00, 0xFF0000DF, 0xFFDF00DF, 0xFF00DFDF, 0xFFDFDFDF, 56 | }; 57 | const uint32_t Emulator_CanvasGRBAColors[16 * 8] = 58 | { 59 | 0xFF000000, 0xFF800000, 0xFF000080, 0xFF800080, 0xFF008000, 0xFF808000, 0xFF008080, 0xFF808080, 60 | 0xFF000000, 0xFFFF0000, 0xFF0000FF, 0xFFFF00FF, 0xFF00FF00, 0xFFFFFF00, 0xFF00FFFF, 0xFFFFFFFF, 61 | 0xFF000000, 0xFF600000, 0xFF000080, 0xFF600080, 0xFF008000, 0xFF608000, 0xFF008080, 0xFF608080, 62 | 0xFF000000, 0xFFDF0000, 0xFF0000FF, 0xFFDF00FF, 0xFF00FF00, 0xFFDFFF00, 0xFF00FFFF, 0xFFDFFFFF, 63 | 0xFF000000, 0xFF800000, 0xFF000060, 0xFF800060, 0xFF008000, 0xFF808000, 0xFF008060, 0xFF808060, 64 | 0xFF000000, 0xFFFF0000, 0xFF0000DF, 0xFFFF00DF, 0xFF00FF00, 0xFFFFFF00, 0xFF00FFDF, 0xFFFFFFDF, 65 | 0xFF000000, 0xFF600000, 0xFF000060, 0xFF600060, 0xFF008000, 0xFF608000, 0xFF008060, 0xFF608060, 66 | 0xFF000000, 0xFFDF0000, 0xFF0000DF, 0xFFDF00DF, 0xFF00FF00, 0xFFDFFF00, 0xFF00FFDF, 0xFFDFFFDF, 67 | 0xFF000000, 0xFF800000, 0xFF000080, 0xFF800080, 0xFF006000, 0xFF806000, 0xFF006080, 0xFF806080, 68 | 0xFF000000, 0xFFFF0000, 0xFF0000FF, 0xFFFF00FF, 0xFF00DF00, 0xFFFFDF00, 0xFF00DFFF, 0xFFFFDFFF, 69 | 0xFF000000, 0xFF600000, 0xFF000080, 0xFF600080, 0xFF006000, 0xFF606000, 0xFF006080, 0xFF606080, 70 | 0xFF000000, 0xFFDF0000, 0xFF0000FF, 0xFFDF00FF, 0xFF00DF00, 0xFFDFDF00, 0xFF00DFFF, 0xFFDFDFFF, 71 | 0xFF000000, 0xFF800000, 0xFF000060, 0xFF800060, 0xFF006000, 0xFF806000, 0xFF006060, 0xFF806060, 72 | 0xFF000000, 0xFFFF0000, 0xFF0000DF, 0xFFFF00DF, 0xFF00DF00, 0xFFFFDF00, 0xFF00DFDF, 0xFFFFDFDF, 73 | 0xFF000000, 0xFF600000, 0xFF000060, 0xFF600060, 0xFF006000, 0xFF606000, 0xFF006060, 0xFF606060, 74 | 0xFF000000, 0xFFDF0000, 0xFF0000DF, 0xFFDF00DF, 0xFF00DF00, 0xFFDFDF00, 0xFF00DFDF, 0xFFDFDFDF, 75 | }; 76 | const uint32_t Emulator_CanvasGrayColors[16 * 8] = 77 | { 78 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 79 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 80 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 81 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 82 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 83 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 84 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 85 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 86 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 87 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 88 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 89 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 90 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 91 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 92 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 93 | 0xFF000000, 0xFF242424, 0xFF484848, 0xFF6C6C6C, 0xFF909090, 0xFFB4B4B4, 0xFFD8D8D8, 0xFFFFFFFF, 94 | }; 95 | 96 | #include "uknc_rom.h" 97 | 98 | 99 | ////////////////////////////////////////////////////////////////////// 100 | 101 | 102 | CMotherboard* g_pBoard = NULL; 103 | 104 | bool g_okEmulatorInitialized = false; 105 | bool g_okEmulatorRunning = false; 106 | 107 | 108 | bool m_okEmulatorSound = false; 109 | 110 | void* g_pFrameBuffer = 0; 111 | 112 | long m_nFrameCount = 0; 113 | uint32_t m_dwTickCount = 0; 114 | uint32_t m_dwEmulatorUptime = 0; // UKNC uptime, seconds, from turn on or reset, increments every 25 frames 115 | long m_nUptimeFrameCount = 0; 116 | 117 | ////////////////////////////////////////////////////////////////////// 118 | 119 | 120 | const int KEYEVENT_QUEUE_SIZE = 32; 121 | uint16_t m_ScreenKeyQueue[KEYEVENT_QUEUE_SIZE]; 122 | int m_ScreenKeyQueueTop = 0; 123 | int m_ScreenKeyQueueBottom = 0; 124 | int m_ScreenKeyQueueCount = 0; 125 | 126 | void ScreenView_PutKeyEventToQueue(uint16_t keyevent) 127 | { 128 | if (m_ScreenKeyQueueCount == KEYEVENT_QUEUE_SIZE) return; // Full queue 129 | 130 | m_ScreenKeyQueue[m_ScreenKeyQueueTop] = keyevent; 131 | m_ScreenKeyQueueTop++; 132 | if (m_ScreenKeyQueueTop >= KEYEVENT_QUEUE_SIZE) 133 | m_ScreenKeyQueueTop = 0; 134 | m_ScreenKeyQueueCount++; 135 | } 136 | uint16_t ScreenView_GetKeyEventFromQueue() 137 | { 138 | if (m_ScreenKeyQueueCount == 0) return 0; // Empty queue 139 | 140 | uint16_t keyevent = m_ScreenKeyQueue[m_ScreenKeyQueueBottom]; 141 | m_ScreenKeyQueueBottom++; 142 | if (m_ScreenKeyQueueBottom >= KEYEVENT_QUEUE_SIZE) 143 | m_ScreenKeyQueueBottom = 0; 144 | m_ScreenKeyQueueCount--; 145 | 146 | return keyevent; 147 | } 148 | void ScreenView_ProcessKeyboard() 149 | { 150 | // Process next event in the keyboard queue 151 | uint16_t keyevent = ScreenView_GetKeyEventFromQueue(); 152 | if (keyevent != 0) 153 | { 154 | bool pressed = ((keyevent & 0x8000) != 0); 155 | bool ctrl = ((keyevent & 0x4000) != 0); 156 | uint8_t scan = (uint8_t)(keyevent & 0x7f); 157 | printf(_T("KeyEvent: %04o %d %d\r\n"), scan, pressed, ctrl); 158 | 159 | g_pBoard->KeyboardEvent(scan, pressed); 160 | } 161 | } 162 | 163 | 164 | #ifdef __cplusplus 165 | extern "C" { 166 | #endif 167 | 168 | EMSCRIPTEN_KEEPALIVE void Emulator_Init() 169 | { 170 | printf("Emulator_Init()\n"); 171 | 172 | g_pFrameBuffer = malloc(640 * 288 * 4); 173 | 174 | ASSERT(g_pBoard == NULL); 175 | 176 | CProcessor::Init(); 177 | 178 | g_pBoard = new CMotherboard(); 179 | 180 | // Load ROM file 181 | uint8_t buffer[32768]; 182 | memset(buffer, 0, 32768); 183 | memcpy(buffer, uknc_rom, uknc_rom_length); 184 | g_pBoard->LoadROM(buffer); 185 | 186 | g_pBoard->Reset(); 187 | 188 | g_okEmulatorInitialized = true; 189 | } 190 | 191 | EMSCRIPTEN_KEEPALIVE uint32_t Emulator_GetUptime() 192 | { 193 | return m_dwEmulatorUptime; 194 | } 195 | 196 | EMSCRIPTEN_KEEPALIVE uint16_t Emulator_GetReg() 197 | { 198 | return g_pBoard->GetPPU()->GetPC(); 199 | } 200 | 201 | EMSCRIPTEN_KEEPALIVE void Emulator_Start() 202 | { 203 | printf("Emulator_Start()\n"); 204 | 205 | g_okEmulatorRunning = true; 206 | } 207 | EMSCRIPTEN_KEEPALIVE void Emulator_Stop() 208 | { 209 | printf("Emulator_Stop()\n"); 210 | 211 | g_okEmulatorRunning = false; 212 | } 213 | EMSCRIPTEN_KEEPALIVE void Emulator_Reset() 214 | { 215 | printf("Emulator_Reset()\n"); 216 | 217 | ASSERT(g_pBoard != NULL); 218 | 219 | g_pBoard->Reset(); 220 | } 221 | 222 | EMSCRIPTEN_KEEPALIVE void Emulator_Unzip(const char* archivename, const char* filename) 223 | { 224 | struct zip_t* zip = zip_open(archivename, 0, 'r'); 225 | 226 | zip_entry_openbyindex(zip, 0); 227 | 228 | zip_entry_fread(zip, filename); 229 | 230 | zip_entry_close(zip); 231 | 232 | zip_close(zip); 233 | 234 | remove(archivename); 235 | } 236 | 237 | EMSCRIPTEN_KEEPALIVE void Emulator_DetachFloppyImage(int slot) 238 | { 239 | g_pBoard->DetachFloppyImage(slot); 240 | 241 | char buffer[6]; 242 | buffer[0] = '/'; 243 | buffer[1] = 'd'; 244 | buffer[2] = 's'; 245 | buffer[3] = 'k'; 246 | buffer[4] = slot + '0'; 247 | buffer[5] = 0; 248 | 249 | remove(buffer); 250 | } 251 | 252 | EMSCRIPTEN_KEEPALIVE void Emulator_AttachFloppyImage(int slot) 253 | { 254 | char buffer[6]; 255 | buffer[0] = '/'; 256 | buffer[1] = 'd'; 257 | buffer[2] = 's'; 258 | buffer[3] = 'k'; 259 | buffer[4] = slot + '0'; 260 | buffer[5] = 0; 261 | 262 | g_pBoard->AttachFloppyImage(slot, buffer); 263 | } 264 | 265 | EMSCRIPTEN_KEEPALIVE void Emulator_UnloadROMCartridge(int slot) 266 | { 267 | g_pBoard->UnloadROMCartridge(slot); 268 | } 269 | 270 | EMSCRIPTEN_KEEPALIVE void Emulator_LoadROMCartridge(int slot) 271 | { 272 | char buffer[6]; 273 | buffer[0] = '/'; 274 | buffer[1] = 'c'; 275 | buffer[2] = 'a'; 276 | buffer[3] = 'r'; 277 | buffer[4] = slot + '0'; 278 | buffer[5] = 0; 279 | 280 | FILE* fp = fopen(buffer, "rb"); 281 | if (fp == 0) 282 | return; 283 | uint8_t* pImage = static_cast(calloc(24 * 1024, 1)); 284 | size_t dwBytesRead = fread(pImage, 1, 24 * 1024, fp); 285 | fclose(fp); 286 | remove(buffer); 287 | if (dwBytesRead != 24 * 1024) 288 | { 289 | free(pImage); 290 | return; 291 | } 292 | 293 | g_pBoard->LoadROMCartridge(slot, pImage); 294 | 295 | ::free(pImage); 296 | } 297 | 298 | EMSCRIPTEN_KEEPALIVE void Emulator_SystemFrame() 299 | { 300 | //printf("Emulator_SystemFrame()\n"); 301 | 302 | //SoundGen_SetVolume(Settings_GetSoundVolume()); 303 | 304 | g_pBoard->SetCPUBreakpoints(nullptr); 305 | g_pBoard->SetPPUBreakpoints(nullptr); 306 | 307 | ScreenView_ProcessKeyboard(); 308 | 309 | if (!g_pBoard->SystemFrame()) 310 | return; 311 | 312 | // Calculate emulator uptime (25 frames per second) 313 | m_nUptimeFrameCount++; 314 | if (m_nUptimeFrameCount >= 25) 315 | { 316 | m_dwEmulatorUptime++; 317 | m_nUptimeFrameCount = 0; 318 | } 319 | } 320 | 321 | EMSCRIPTEN_KEEPALIVE void* Emulator_PrepareScreen(int screenViewMode) 322 | { 323 | //printf("Emulator_PrepareScreen(%d)\n", screenViewMode); 324 | if (g_pFrameBuffer == 0) 325 | printf("Emulator_PrepareScreen() null framebuffer\n"); 326 | 327 | const uint32_t* palette = Emulator_CanvasGRBAColors; 328 | switch ((ScreenViewMode)screenViewMode) 329 | { 330 | case RGBScreen: 331 | palette = Emulator_CanvasRGBAColors; break; 332 | case GrayScreen: 333 | palette = Emulator_CanvasGrayColors; break; 334 | case GRBScreen: 335 | palette = Emulator_CanvasGRBAColors; break; 336 | } 337 | 338 | Emulator_PrepareScreenRGB32(g_pFrameBuffer, palette); 339 | 340 | return g_pFrameBuffer; 341 | } 342 | 343 | EMSCRIPTEN_KEEPALIVE void Emulator_KeyEvent(uint8_t keyscan, bool pressed) 344 | { 345 | //printf("Emulator_KeyEvent(%04o, %d)\n", keyscan, pressed); 346 | ScreenView_PutKeyEventToQueue(uint16_t(keyscan) | (pressed ? 0x8000 : 0)); 347 | } 348 | 349 | EMSCRIPTEN_KEEPALIVE void Emulator_LoadImage() 350 | { 351 | const char * imageFileName = "/image"; 352 | 353 | Emulator_Stop(); 354 | 355 | // Open file 356 | FILE* fpFile = ::fopen(imageFileName, "rb"); 357 | if (fpFile == NULL) 358 | { 359 | remove(imageFileName); 360 | printf("Emulator_LoadImage(): failed to open file\n"); 361 | return; 362 | } 363 | 364 | // Read header 365 | uint32_t bufHeader[UKNCIMAGE_HEADER_SIZE / sizeof(uint32_t)]; 366 | uint32_t dwBytesRead = ::fread(bufHeader, 1, UKNCIMAGE_HEADER_SIZE, fpFile); 367 | if (dwBytesRead != UKNCIMAGE_HEADER_SIZE) 368 | { 369 | ::fclose(fpFile); 370 | remove(imageFileName); 371 | printf("Emulator_LoadImage(): failed to read file\n"); 372 | return; 373 | } 374 | 375 | // Allocate memory 376 | uint8_t* pImage = (uint8_t*) ::malloc(UKNCIMAGE_SIZE); 377 | if (pImage == NULL) 378 | { 379 | ::fclose(fpFile); 380 | remove(imageFileName); 381 | printf("Emulator_LoadImage(): malloc failed\n"); 382 | return; 383 | } 384 | 385 | // Read image 386 | ::fseek(fpFile, 0, SEEK_SET); 387 | dwBytesRead = ::fread(pImage, 1, UKNCIMAGE_SIZE, fpFile); 388 | ::fclose(fpFile); 389 | remove(imageFileName); 390 | if (dwBytesRead != UKNCIMAGE_SIZE) 391 | { 392 | ::free(pImage); 393 | printf("Emulator_LoadImage(): failed to read file\n"); 394 | return; 395 | } 396 | 397 | // Restore emulator state from the image 398 | g_pBoard->LoadFromImage(pImage); 399 | 400 | m_dwEmulatorUptime = *(uint32_t*)(pImage + 16); 401 | 402 | // Free memory 403 | ::free(pImage); 404 | printf("Emulator_LoadImage() done\n"); 405 | } 406 | 407 | EMSCRIPTEN_KEEPALIVE void Emulator_SaveImage() 408 | { 409 | const char * imageFileName = "/image"; 410 | 411 | // Open file 412 | FILE* fpFile = ::fopen(imageFileName, "w+b"); 413 | if (fpFile == NULL) 414 | { 415 | printf("Emulator_SaveImage(): failed to open file\n"); 416 | return; 417 | } 418 | 419 | // Allocate memory 420 | uint8_t* pImage = static_cast(::calloc(UKNCIMAGE_SIZE, 1)); 421 | if (pImage == nullptr) 422 | { 423 | ::fclose(fpFile); 424 | printf("Emulator_SaveImage(): calloc failed\n"); 425 | return; 426 | } 427 | 428 | // Prepare header 429 | uint32_t* pHeader = (uint32_t*)pImage; 430 | *pHeader++ = UKNCIMAGE_HEADER1; 431 | *pHeader++ = UKNCIMAGE_HEADER2; 432 | *pHeader++ = UKNCIMAGE_VERSION; 433 | *pHeader++ = UKNCIMAGE_SIZE; 434 | // Store emulator state to the image 435 | g_pBoard->SaveToImage(pImage); 436 | *(uint32_t*)(pImage + 16) = m_dwEmulatorUptime; 437 | 438 | // Save image to the file 439 | size_t dwBytesWritten = ::fwrite(pImage, 1, UKNCIMAGE_SIZE, fpFile); 440 | ::free(pImage); 441 | ::fclose(fpFile); 442 | if (dwBytesWritten != UKNCIMAGE_SIZE) 443 | { 444 | printf("Emulator_SaveImage(): failed to write file\n"); 445 | return; 446 | } 447 | } 448 | 449 | #ifdef __cplusplus 450 | } 451 | #endif 452 | 453 | int main() 454 | { 455 | Emulator_Init(); 456 | } 457 | 458 | void Emulator_PrepareScreenRGB32(void* pImageBits, const uint32_t* colors) 459 | { 460 | if (pImageBits == NULL) return; 461 | if (!g_okEmulatorInitialized) return; 462 | 463 | // Tag parsing loop 464 | uint8_t cursorYRGB = 0; 465 | bool okCursorType = false; 466 | uint8_t cursorPos = 128; 467 | bool cursorOn = false; 468 | uint8_t cursorAddress = 0; // Address of graphical cursor 469 | uint16_t address = 0000270; // Tag sequence start address 470 | bool okTagSize = false; // Tag size: TRUE - 4-uint16_t, false - 2-uint16_t (first tag is always 2-uint16_t) 471 | bool okTagType = false; // Type of 4-uint16_t tag: TRUE - set palette, false - set params 472 | int scale = 1; // Horizontal scale: 1, 2, 4, or 8 473 | uint32_t palette = 0; // Palette 474 | uint32_t palettecurrent[8]; memset(palettecurrent, 0, sizeof(palettecurrent)); // Current palette; update each time we change the "palette" variable 475 | uint8_t pbpgpr = 0; // 3-bit Y-value modifier 476 | for (int yy = 0; yy < 307; yy++) 477 | { 478 | if (okTagSize) // 4-uint16_t tag 479 | { 480 | uint16_t tag1 = g_pBoard->GetRAMWord(0, address); 481 | address += 2; 482 | uint16_t tag2 = g_pBoard->GetRAMWord(0, address); 483 | address += 2; 484 | 485 | if (okTagType) // 4-uint16_t palette tag 486 | { 487 | palette = ((uint32_t)tag1) | ((uint32_t)tag2 << 16); 488 | } 489 | else // 4-uint16_t params tag 490 | { 491 | scale = (tag2 >> 4) & 3; // Bits 4-5 - new scale value 492 | pbpgpr = (uint8_t)((7 - (tag2 & 7)) << 4); // Y-value modifier 493 | cursorYRGB = (uint8_t)(tag1 & 15); // Cursor color 494 | okCursorType = ((tag1 & 16) != 0); // true - graphical cursor, false - symbolic cursor 495 | //ASSERT(okCursorType == 0); //DEBUG 496 | cursorPos = (uint8_t)(((tag1 >> 8) >> scale) & 0x7f); // Cursor position in the line 497 | cursorAddress = (uint8_t)((tag1 >> 5) & 7); 498 | scale = 1 << scale; 499 | } 500 | for (uint8_t c = 0; c < 8; c++) // Update palettecurrent 501 | { 502 | uint8_t valueYRGB = (uint8_t) (palette >> (c << 2)) & 15; 503 | palettecurrent[c] = colors[pbpgpr | valueYRGB]; 504 | //if (pbpgpr != 0) DebugLogFormat(_T("pbpgpr %02x\r\n"), pbpgpr | valueYRGB); 505 | } 506 | } 507 | 508 | uint16_t addressBits = g_pBoard->GetRAMWord(0, address); // The uint16_t before the last uint16_t - is address of bits from all three memory planes 509 | address += 2; 510 | 511 | // Calculate size, type and address of the next tag 512 | uint16_t tagB = g_pBoard->GetRAMWord(0, address); // Last uint16_t of the tag - is address and type of the next tag 513 | okTagSize = (tagB & 2) != 0; // Bit 1 shows size of the next tag 514 | if (okTagSize) 515 | { 516 | address = tagB & ~7; 517 | okTagType = (tagB & 4) != 0; // Bit 2 shows type of the next tag 518 | } 519 | else 520 | address = tagB & ~3; 521 | if ((tagB & 1) != 0) 522 | cursorOn = !cursorOn; 523 | 524 | // Draw bits into the bitmap, from line 20 to line 307 525 | if (yy < 19 /*|| yy > 306*/) 526 | continue; 527 | 528 | // Loop thru bits from addressBits, planes 0,1,2 529 | // For each pixel: 530 | // Get bit from planes 0,1,2 and make value 531 | // Map value to palette; result is 4-bit value YRGB 532 | // Translate value to 24-bit RGB 533 | // Put value to m_bits; repeat using scale value 534 | 535 | int xr = 640; 536 | int y = yy - 19; 537 | uint32_t* pBits = ((uint32_t*)pImageBits) + y * 640; 538 | int pos = 0; 539 | for (;;) 540 | { 541 | // Get bit from planes 0,1,2 542 | uint8_t src0 = g_pBoard->GetRAMByte(0, addressBits); 543 | uint8_t src1 = g_pBoard->GetRAMByte(1, addressBits); 544 | uint8_t src2 = g_pBoard->GetRAMByte(2, addressBits); 545 | // Loop through the bits of the byte 546 | int bit = 0; 547 | for (;;) 548 | { 549 | uint32_t valueRGB; 550 | if (cursorOn && (pos == cursorPos) && (!okCursorType || (okCursorType && bit == cursorAddress))) 551 | valueRGB = colors[cursorYRGB]; // 4-bit to 32-bit color 552 | else 553 | { 554 | // Make 3-bit value from the bits 555 | uint8_t value012 = (src0 & 1) | ((src1 & 1) << 1) | ((src2 & 1) << 2); 556 | valueRGB = palettecurrent[value012]; // 3-bit to 32-bit color 557 | } 558 | 559 | // Put value to m_bits; repeat using scale value 560 | //WAS: for (int s = 0; s < scale; s++) *pBits++ = valueRGB; 561 | switch (scale) 562 | { 563 | case 8: 564 | *pBits++ = valueRGB; 565 | *pBits++ = valueRGB; 566 | *pBits++ = valueRGB; 567 | *pBits++ = valueRGB; 568 | case 4: 569 | *pBits++ = valueRGB; 570 | *pBits++ = valueRGB; 571 | case 2: 572 | *pBits++ = valueRGB; 573 | case 1: 574 | *pBits++ = valueRGB; 575 | default: 576 | break; 577 | } 578 | 579 | xr -= scale; 580 | 581 | if (bit == 7) 582 | break; 583 | bit++; 584 | 585 | // Shift to the next bit 586 | src0 >>= 1; 587 | src1 >>= 1; 588 | src2 >>= 1; 589 | } 590 | if (xr <= 0) 591 | break; // End of line 592 | addressBits++; // Go to the next uint8_t 593 | pos++; 594 | } 595 | } 596 | } 597 | 598 | ////////////////////////////////////////////////////////////////////// 599 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | UKNCBTL WASM 7 | 180 | 181 | 182 |
emscripten
183 |
Downloading...
184 |
185 | 186 |
187 | 188 |
189 |
190 |
191 |
192 | UKNCBTL 193 |
194 | 195 | 196 | 197 | 198 | 199 |
200 | 201 | 202 |
203 |
204 |
205 | Colors: 206 |
207 | 208 | 209 |
210 |
211 | 212 | 213 |
214 |
215 | 216 | 217 |
218 |
219 |
220 |
221 | Disks: 222 |
223 |
224 | 0: 225 | 226 | 227 | 228 |
229 |
230 | 1: 231 | 232 | 233 | 234 |
235 |
236 | 2: 237 | 238 | 239 | 240 |
241 |
242 | 3: 243 | 244 | 245 | 246 |
247 |
248 | Cartridges: 249 |
250 |
251 | 1: 252 | 253 | 254 | 255 |
256 |
257 | 2: 258 | 259 | 260 | 261 |
262 |
263 |
264 |
265 | 266 |
267 |
268 | 269 |
270 | 271 | 0010 272 | 0011 273 | 0012 274 | 0014 275 | 0015 276 | 0172 277 | 0152 278 | 0151 279 | 0171 280 | 0004 281 | 282 | 0006 283 | 0007 284 | 0030 285 | 0031 286 | 0032 287 | 0013 288 | 0034 289 | 0035 290 | 0016 291 | 0017 292 | 0177 293 | 0176 294 | 0175 295 | 0173 296 | 0132 297 | 298 | 0026 299 | 0027 300 | 0050 301 | 0051 302 | 0052 303 | 0033 304 | 0054 305 | 0055 306 | 0036 307 | 0037 308 | 0157 309 | 0156 310 | 0155 311 | 0174 312 | 313 | 0046 314 | 0047 315 | 0070 316 | 0071 317 | 0072 318 | 0053 319 | 0074 320 | 0075 321 | 0056 322 | 0057 323 | 0137 324 | 0136 325 | 0135 326 | 327 | 0106 328 | 0066 329 | 0067 330 | 0110 331 | 0111 332 | 0112 333 | 0073 334 | 0114 335 | 0115 336 | 0076 337 | 0077 338 | 0117 339 | 340 | 0105 341 | 0107 342 | 0113 343 | 0133 344 | 345 | 0153 346 | 347 | 0116 348 | 0154 349 | 0134 350 | 0133 351 | 352 | 0131 353 | 0025 354 | 0005 355 | 0125 356 | 0145 357 | 0165 358 | 0130 359 | 0150 360 | 0170 361 | 0127 362 | 0147 363 | 0167 364 | 0126 365 | 0146 366 | 0166 367 | 368 |
369 | 370 |
371 | 372 | 765 | 766 | 767 | 768 | --------------------------------------------------------------------------------