emu: it is now possible to activate the console from memview and sprview
[zymosis.git] / src / ZXEmuT / memview.c
blob31151ccf8d5a0d867fca8cb75148c1532e58d5d6
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2022 Ketmar Dark <ketmar@ketmar.no-ip.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
20 #define MV_FAST_RECODE
22 #include "../libzymosis/zymosis.h"
23 #include "libvideo/video.h"
24 #include "memview.h"
25 #include "emucommon.h"
26 #include "emuvars.h"
27 #include "emuutils.h"
28 #include "console.h"
31 // ////////////////////////////////////////////////////////////////////////// //
32 #ifdef MV_FAST_RECODE
33 static const uint8_t cp1251to866[256] = {
34 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
35 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
36 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
37 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
38 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
39 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
40 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
41 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
42 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
43 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
44 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
45 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
46 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
47 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
48 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
49 0xa8,0xb8,0xaa,0xba,0xaf,0xbf,0xa1,0xa2,0xb0,0x00,0xb7,0x00,0xb9,0xa4,0x00,0xa0,
52 #else
54 static const uint16_t charMap1251[128] = {
55 0x0402,0x0403,0x201A,0x0453,0x201E,0x2026,0x2020,0x2021,0x20AC,0x2030,0x0409,0x2039,0x040A,0x040C,0x040B,0x040F,
56 0x0452,0x2018,0x2019,0x201C,0x201D,0x2022,0x2013,0x2014,0x003F,0x2122,0x0459,0x203A,0x045A,0x045C,0x045B,0x045F,
57 0x00A0,0x040E,0x045E,0x0408,0x00A4,0x0490,0x00A6,0x00A7,0x0401,0x00A9,0x0404,0x00AB,0x00AC,0x00AD,0x00AE,0x0407,
58 0x00B0,0x00B1,0x0406,0x0456,0x0491,0x00B5,0x00B6,0x00B7,0x0451,0x2116,0x0454,0x00BB,0x0458,0x0405,0x0455,0x0457,
59 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,
60 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,
61 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,
62 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F,
65 static const uint16_t charMap866[128] = {
66 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,
67 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,
68 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,
69 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510,
70 0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567,
71 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580,
72 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F,
73 0x0401,0x0451,0x0404,0x0454,0x0407,0x0457,0x040E,0x045E,0x00B0,0x2219,0x00B7,0x221A,0x2116,0x00A4,0x25A0,0x00A0,
77 static uint8_t table1251to866[65536];
78 static int tableRCInited = 0;
81 //==========================================================================
83 // initTableRC
85 //==========================================================================
86 static void initTableRC (void) {
87 tableRCInited = 1;
88 memset(table1251to866, '.', sizeof(table1251to866));
89 for (uint16_t f = 0; f < 128; ++f) table1251to866[f] = (uint8_t)f;
90 for (uint16_t f = 0; f < 128; ++f) {
91 const uint16_t code = charMap1251[f];
92 for (uint16_t c = 0; c < 128; ++c) {
93 if (charMap866[c] == code) {
94 table1251to866[f+128U] = (uint8_t)(c+128U);
99 #endif
102 // ////////////////////////////////////////////////////////////////////////// //
103 int memviewActive = 0;
104 uint8_t mv_pagemap[256];
106 // >0xffff: pages
107 uint32_t mv_staddr = 0x5B00;
108 int mv_encoding = MV_ENC_7BIT;
109 int mv_viewmode = MV_MODE_HEX;
110 int mv_textwidth = 64;
111 uint8_t mv_textcolor = 7; // paper is ignored
113 #define BOOKMARK_NONE (~((uint32_t)0))
115 static uint32_t bookmarks[10];
117 static int inaddrpos = -1;
118 static int mmeditpos = 0;
119 static int mmeditactive = 0;
121 static vt_simple_menu *current_menu = NULL;
122 static void (*menu_sel_cb) (uint32_t sel) = NULL;
123 static vt_simple_menu wkmenu;
125 static int evtCtrlKeyDown = 0;
126 static int evtAltKeyDown = 0;
127 static int evtShiftKeyDown = 0;
130 //==========================================================================
132 // recodeByte
134 //==========================================================================
135 static inline uint16_t recodeByte (uint16_t b) {
136 if (mv_encoding == MV_ENC_1251) {
137 #ifdef MV_FAST_RECODE
138 b = cp1251to866[b];
139 #else
140 if (!tableRCInited) initTableRC();
141 b = table1251to866[b];
142 #endif
144 if (mv_encoding == MV_ENC_7BIT) b &= 0x7f;
145 if (b < 32 || b == 127) b = '.'|0x8000;
146 else if (b == 32 && mv_viewmode == MV_MODE_HEX) b = '.'|0x4000;
147 return b;
151 //==========================================================================
153 // isAddrEditActive
155 //==========================================================================
156 static __attribute__((always_inline)) inline int isAddrEditActive (void) {
157 return (inaddrpos >= 0);
161 //==========================================================================
163 // isMemEditActive
165 //==========================================================================
166 static __attribute__((always_inline)) inline int isMMEditActive (void) {
167 return mmeditactive;
171 //==========================================================================
173 // getByte
175 //==========================================================================
176 static __attribute__((always_inline)) inline uint8_t getByte (uint32_t addr) {
177 return
178 addr <= 0xffffU ?
179 z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER) :
180 zxMemoryBanks[mv_pagemap[((addr>>14)-4)%(unsigned)zxMaxMemoryBank]][addr&0x3fffU];
184 //==========================================================================
186 // memviewResetMMap
188 //==========================================================================
189 static void memviewResetMMap (int inorder) {
190 for (uint32_t f = 0; f < 256; ++f) mv_pagemap[f] = f;
191 if (!inorder) {
192 mv_pagemap[1] = 5;
193 mv_pagemap[5] = 1;
198 //==========================================================================
200 // memviewInit
202 //==========================================================================
203 void memviewInit (void) {
204 memviewResetMMap(1);
205 for (unsigned f = 0; f < 10; ++f) bookmarks[f] = BOOKMARK_NONE;
209 //==========================================================================
211 // memviewSetActive
213 //==========================================================================
214 void memviewSetActive (int st) {
215 memviewActive = !!st;
219 //==========================================================================
221 // drawAllChars
223 //==========================================================================
224 static __attribute__((unused)) void drawAllChars (void) {
225 for (int y = 0; y < 16; ++y) {
226 for (int x = 0; x < 16; ++x) {
227 vt_writechar(x, y, y*16+x, 0x0f);
230 for (int x = 0; x < 16; ++x) {
231 vt_writechar(x, VID_TEXT_HEIGHT-1, '*', (x<<4)|(x < 8 ? 15 : 0));
236 //==========================================================================
238 // rescanPages
240 //==========================================================================
241 static void rescanPages (void) {
242 for (int f = 0; f < zxMaxMemoryBank; ++f) {
243 zxMemBanksDirty[f] = 0;
244 const uint8_t *mbb = zxMemoryBanks[f];
245 for (uint32_t pos = 0; pos < 0x4000; ++pos, ++mbb) {
246 if (*mbb) {
247 zxMemBanksDirty[f] = 1;
248 break;
255 //==========================================================================
257 // drawMMap
259 //==========================================================================
260 static void drawMMap (void) {
261 int xofs = 26;
262 int yofs = 10;
264 vt_draw_frame_ex(xofs-2, yofs-1, 3*8+3, 10, 0x50,
265 VID_FRAME_DOUBLE|VID_FRAME_OVERWRITE|VID_FRAME_FILL|VID_FRAME_SHADOW,
266 " Page Map ");
268 for (int y = 0; y < 8; ++y) {
269 for (int x = 0; x < 8; ++x) {
270 uint8_t attr;
271 if (y*8+x == mmeditpos/2) {
272 attr = 0x1F;
273 } else {
274 attr = (zxMemBanksDirty[mv_pagemap[y*8+x]] ? 0x50 : 0x51);
276 vt_writef(xofs+x*3, yofs+y, attr, "%02X", mv_pagemap[y*8+x]);
280 const int cx = mmeditpos%16;
281 const int cy = mmeditpos/16;
282 vt_writeattr(xofs+(cx/2)*3+(cx%2), yofs+cy, emuGetCursorColor(0xF0, 0x70));
286 //==========================================================================
288 // writeAddrAt
290 //==========================================================================
291 static void writeAddrAt (int x, int y, uint32_t addr) {
292 uint8_t pg;
293 char buf[16];
294 if (addr <= 0xffffU) {
295 snprintf(buf, sizeof(buf), ".. %04X", addr);
296 pg = zxMemoryBankNum[addr>>14];
297 } else {
298 pg = ((addr>>14)-4)%(unsigned)zxMaxMemoryBank;
299 snprintf(buf, sizeof(buf), "%02X %04X", pg, addr&0x3fffU);
300 pg = mv_pagemap[pg];
302 uint8_t attr = 0x05;
303 for (unsigned f = 0; f < 10; ++f) {
304 if (bookmarks[f] == addr) {
305 attr |= 0x10;
306 break;
309 vt_writestrcnt(x, y, buf, 7, attr);
310 if (!zxMemBanksDirty[pg]) vt_writeattrs(x, y, 2, 0x03|(attr&0xf0));
314 //==========================================================================
316 // drawHexView
318 //==========================================================================
319 static void drawHexView (void) {
320 char buf[256];
321 const int mhgt = VID_TEXT_HEIGHT-2;
322 uint32_t addr = mv_staddr;
323 vt_draw_frame(1, 0, 78, mhgt+2, 15, VID_FRAME_SINGLE);
324 const int xofs = 2;
325 const int yofs = 1;
326 for (int dy = 0; dy < mhgt; ++dy) {
327 writeAddrAt(xofs, dy+yofs, addr);
329 for (int dx = 0; dx < 16; ++dx) {
330 if (addr <= 0xffffU && addr+(unsigned)dx > 0xffffU) break;
331 uint8_t b = getByte(addr+(unsigned)dx);
332 snprintf(buf, sizeof(buf), "%02X", b);
333 vt_writestrz(xofs+8+dx*3+(dx >= 8), dy+yofs, buf, 7);
336 for (int dx = 0; dx < 16; ++dx) {
337 if (addr <= 0xffffU && addr+(unsigned)dx > 0xffffU) break;
338 uint16_t b = recodeByte(getByte(addr+(unsigned)dx));
339 vt_writechar(xofs+8+16*3+2+dx+(dx >= 8), dy+yofs, (char)b,
340 (b <= 0xff ? mv_textcolor : (b&0x8000 ? 5 : mv_textcolor)+(b&0x4000 ? 0x10 : 0)));
343 addr += 16;
348 //==========================================================================
350 // goLineUp
352 //==========================================================================
353 static uint32_t goLineUp (uint32_t addr) {
354 if (!addr) return 0;
355 uint8_t b = getByte(addr-1U);
356 const int skiptsp = (b == 10 || b == 13);
357 if (b == 10) {
358 --addr;
359 if (addr && getByte(addr-1U) == 13) --addr;
360 if (!addr) return 0;
361 } else if (b == 13) {
362 --addr;
363 if (!addr) return 0;
365 int zerostate = -1; // <0:haven't seen; 0:non-zero seen; 1:only zero seen
366 // skip trailing spaces
367 if (skiptsp) {
368 while (addr && getByte(addr-1U) == 32) {
369 --addr;
370 zerostate = 0;
373 const uint32_t orgaddr = addr;
374 int count = mv_textwidth;
375 while (addr && count--) {
376 b = getByte(addr-1U);
377 if (b == 13 || b == 10) return addr;
378 if (b == 0) {
379 if (zerostate == 0) return addr; // seen non-zero
380 zerostate = 1; // only zero seen
381 } else {
382 if (zerostate == 1) return addr;
383 zerostate = 0; // seen non-zero
385 --addr;
387 return (addr ? orgaddr-mv_textwidth : 0);
391 //==========================================================================
393 // goLineDown
395 //==========================================================================
396 static uint32_t goLineDown (uint32_t addr) {
397 const uint32_t orgaddr = addr;
398 int count = mv_textwidth;
399 int zerostate = -1; // <0:haven't seen; 0:non-zero seen; 1:only zero seen
400 uint8_t b;
401 while (addr && count--) {
402 b = getByte(addr++);
403 if (b == 13 || b == 10) {
404 if (b == 13 && getByte(addr) == 10) ++addr;
405 return addr;
407 if (b == 0) {
408 if (zerostate == 0) return addr-1; // seen non-zero
409 zerostate = 1; // only zero seen
410 } else {
411 if (zerostate == 1) return addr-1;
412 zerostate = 0; // seen non-zero
415 addr = orgaddr+mv_textwidth;
416 // check for trailing spaces
417 while (getByte(addr) == 32) ++addr;
418 b = getByte(addr);
419 if (b == 13) b = getByte(++addr);
420 if (b == 10) ++addr;
421 return addr;
425 //==========================================================================
427 // drawTextView
429 //==========================================================================
430 static void drawTextView (void) {
431 const int xofs = 2;
432 const int yofs = 1;
433 uint32_t addr = mv_staddr;
434 vt_draw_frame(1, 0, 78, VID_TEXT_HEIGHT, 15, VID_FRAME_SINGLE);
435 for (int y = 0; y < VID_TEXT_HEIGHT-2; ++y) {
436 writeAddrAt(xofs, y+yofs, addr);
437 uint32_t eaddr = goLineDown(addr);
438 if (eaddr > addr && getByte(eaddr-1) == 10) --eaddr;
439 if (eaddr > addr && getByte(eaddr-1) == 13) --eaddr;
440 const uint32_t naddr = eaddr;
441 if (eaddr-addr > mv_textwidth) eaddr = addr+mv_textwidth;
442 int xxofs = (MV_MAX_TEXT_WIDTH-mv_textwidth)/2;
443 const uint32_t len = eaddr-addr;
444 for (uint32_t x = 0; x < len; ++x) {
445 uint16_t b = recodeByte(getByte(addr++));
446 vt_writechar(xofs+8+xxofs+x, y+yofs, (char)b,
447 (b <= 0xff ? mv_textcolor : (b&0x8000 ? 5 : mv_textcolor)+(b&0x4000 ? 0x10 : 0)));
449 // check for trailing spaces
450 addr = naddr;
451 while (getByte(addr) == 32) ++addr;
452 if (getByte(addr) == 13) ++addr;
453 if (getByte(addr) == 10) ++addr;
455 vt_writef(7, 0, 0x05, "[wdt:%d]", mv_textwidth);
459 //==========================================================================
461 // memviewDraw
463 //==========================================================================
464 void memviewDraw (void) {
465 //vt_cls(0xb0, 0x01);
466 vt_cls(32, 0x07);
468 if (mv_viewmode == MV_MODE_HEX) drawHexView(); else drawTextView();
470 switch (mv_encoding) {
471 case MV_ENC_7BIT: vt_writestrz(2+70, 0, "[7bit]", 4); break;
472 case MV_ENC_1251: vt_writestrz(2+68, 0, "[cp1251]", 4); break;
473 case MV_ENC_866: vt_writestrz(2+69, 0, "[cp866]", 4); break;
476 // draw physical page
478 uint8_t pg;
479 if (mv_staddr <= 0xffffU) {
480 pg = zxMemoryBankNum[mv_staddr>>14];
481 } else {
482 pg = mv_pagemap[((mv_staddr>>14)-4)%(unsigned)zxMaxMemoryBank];
484 uint8_t attr = (zxMemBanksDirty[pg] ? 0x04 : 0x03);
485 vt_writef(2, 0, attr, "[%02X]", pg);
488 if (isAddrEditActive()) {
489 vt_writeattr(2+inaddrpos+(inaddrpos >= 2), 1, emuGetCursorColor(0xF0, 0x70));
490 } else if (isMMEditActive()) {
491 drawMMap();
494 if (current_menu) vt_smm_draw(current_menu);
496 #if 0
497 drawAllChars();
498 #endif
502 //==========================================================================
504 // doLeft
506 //==========================================================================
507 static void doLeft (void) {
508 if (isAddrEditActive()) {
509 if (inaddrpos) --inaddrpos;
510 return;
512 if (isMMEditActive()) {
513 if (mmeditpos > 0) {
514 if (evtCtrlKeyDown) {
515 mmeditpos = (mmeditpos&0xfe)-1;
516 if (mmeditpos < 1) mmeditpos = 1;
517 } else {
518 --mmeditpos;
521 return;
523 if (mv_staddr) --mv_staddr;
527 //==========================================================================
529 // doRight
531 //==========================================================================
532 static void doRight (void) {
533 if (isAddrEditActive()) {
534 if (inaddrpos < 5) ++inaddrpos;
535 return;
537 if (isMMEditActive()) {
538 if (evtCtrlKeyDown) {
539 mmeditpos = (mmeditpos&0xfe)+3;
540 if (mmeditpos >= 64*2) mmeditpos = 64*2-1;
541 } else {
542 if (mmeditpos < 64*2-1) ++mmeditpos;
544 return;
546 ++mv_staddr;
550 //==========================================================================
552 // doUp
554 //==========================================================================
555 static void doUp (void) {
556 if (isAddrEditActive()) {
557 if (inaddrpos >= 2) inaddrpos = 0;
558 return;
560 if (isMMEditActive()) {
561 if (mmeditpos >= 8*2) mmeditpos -= 8*2;
562 return;
564 if (mv_viewmode == MV_MODE_TEXT) {
565 uint32_t nn = goLineUp(mv_staddr);
566 if (mv_staddr > 0xffffU && nn <= 0xffffU) nn = 0x010000U;
567 mv_staddr = nn;
568 return;
570 if (mv_staddr <= 0xffffU) {
571 if (mv_staddr < 16) mv_staddr = 0; else mv_staddr -= 16;
572 } else {
573 mv_staddr -= 16;
578 //==========================================================================
580 // doDown
582 //==========================================================================
583 static void doDown (void) {
584 if (isAddrEditActive()) {
585 if (inaddrpos < 2) inaddrpos = 2;
586 return;
588 if (isMMEditActive()) {
589 if (mmeditpos+8*2 < 64*2) mmeditpos += 8*2;
590 return;
592 if (mv_viewmode == MV_MODE_TEXT) {
593 mv_staddr = goLineDown(mv_staddr);
594 return;
596 mv_staddr += 16;
600 //==========================================================================
602 // doPageUp
604 //==========================================================================
605 static void doPageUp (void) {
606 if (isAddrEditActive()) {
607 if (mv_staddr > 0xffffU) {
608 uint32_t pg = (mv_staddr>>14)-4;
609 if (pg > 0) --pg;
610 mv_staddr = (pg+4)<<14;
612 return;
614 if (isMMEditActive()) return;
615 if (mv_viewmode == MV_MODE_TEXT) {
616 for (int f = 0; f < VID_TEXT_HEIGHT-3; ++f) {
617 uint32_t nn = goLineUp(mv_staddr);
618 if (mv_staddr > 0xffffU && nn <= 0xffffU) nn = 0x010000U;
619 mv_staddr = nn;
621 return;
623 if (mv_staddr <= 0xffffU) {
624 if (mv_staddr < 16*(VID_TEXT_HEIGHT-2)) mv_staddr = 0; else mv_staddr -= 16*(VID_TEXT_HEIGHT-2);
625 } else {
626 mv_staddr -= 16*(VID_TEXT_HEIGHT-2);
631 //==========================================================================
633 // doPageDown
635 //==========================================================================
636 static void doPageDown (void) {
637 if (isAddrEditActive()) {
638 if (mv_staddr <= 0xffffU) {
639 mv_staddr = 0x010000U;
640 } else {
641 uint32_t pg = (mv_staddr>>14)-4;
642 if (pg+1 < (uint32_t)zxMaxMemoryBank) mv_staddr = (pg+5)<<14;
644 return;
646 if (isMMEditActive()) return;
647 if (mv_viewmode == MV_MODE_TEXT) {
648 for (int f = 0; f < VID_TEXT_HEIGHT-3; ++f) {
649 mv_staddr = goLineDown(mv_staddr);
651 return;
653 if (mv_staddr <= 0xffffU) {
654 if (mv_staddr+16*(VID_TEXT_HEIGHT-2) > 0xffffU) mv_staddr = 0x10000U-16; else mv_staddr += 16*(VID_TEXT_HEIGHT-2);
655 } else {
656 mv_staddr += 16*(VID_TEXT_HEIGHT-2);
661 //==========================================================================
663 // menu_set_encoding
665 //==========================================================================
666 static void menu_set_encoding (uint32_t sel) {
667 mv_encoding = (int)sel;
671 //==========================================================================
673 // menu_set_text_width
675 //==========================================================================
676 static void menu_set_text_width (uint32_t sel) {
677 const int wdts[5] = {32, 42, 51, 64, MV_MAX_TEXT_WIDTH};
678 mv_textwidth = wdts[sel];
682 //==========================================================================
684 // menu_set_text_bright
686 //==========================================================================
687 static void menu_set_text_bright (uint32_t sel) {
688 mv_textcolor &= 0x07;
689 mv_textcolor |= (sel ? 0x08 : 0x00);
693 //==========================================================================
695 // menu_set_text_color
697 //==========================================================================
698 static void menu_set_text_color (uint32_t sel) {
699 mv_textcolor = (mv_textcolor&0x08)|(sel+1);
703 //==========================================================================
705 // menu_set_view_mode
707 //==========================================================================
708 static void menu_set_view_mode (uint32_t sel) {
709 mv_viewmode = (int)sel;
713 //==========================================================================
715 // memviewKeyEvent
717 //==========================================================================
718 int memviewKeyEvent (SDL_KeyboardEvent *key) {
719 if (current_menu) {
720 int mrs = vt_smm_process_key(current_menu, key);
721 switch (mrs) {
722 case VID_SMM_ESCAPE:
723 vt_smm_deinit(current_menu);
724 current_menu = NULL;
725 return 1;
726 case VID_SMM_NOTMINE:
727 break;
728 case VID_SMM_EATEN:
729 return 1;
730 case VID_SMM_SELECTED:
731 if (menu_sel_cb) {
732 const uint32_t cy = current_menu->cursor_y;
733 vt_smm_deinit(current_menu);
734 current_menu = NULL;
735 menu_sel_cb(cy);
736 } else {
737 vt_smm_deinit(current_menu);
738 current_menu = NULL;
740 return 1;
742 return 1;
745 if (key->type != SDL_KEYDOWN) return 1;
747 evtCtrlKeyDown = !!(key->keysym.mod&KMOD_CTRL);
748 evtAltKeyDown = !!(key->keysym.mod&KMOD_ALT);
749 evtShiftKeyDown = !!(key->keysym.mod&KMOD_SHIFT);
751 if (isAddrEditActive() && key->keysym.unicode > 32 && key->keysym.unicode < 127) {
752 int d = digitInBase((char)key->keysym.unicode, 16);
753 if (d >= 0) {
754 if (inaddrpos < 2) {
755 uint32_t pg = (mv_staddr > 0xffffU ? (mv_staddr>>14)-4 : 0);
756 pg &= (inaddrpos ? 0xf0U : 0x0fU);
757 pg |= ((unsigned)d)<<((1-inaddrpos)*4);
758 if (pg < zxMaxMemoryBank) {
759 mv_staddr = (mv_staddr&0x3fffU)|((pg+4)<<14);
761 } else {
762 if (mv_staddr > 0xffffU && inaddrpos == 2 && d > 3) return 1;
763 if (mv_staddr > 0xffffU) {
764 uint32_t pg = (mv_staddr>>14);
765 mv_staddr &= ~(0x000fU<<((5-inaddrpos)*4));
766 mv_staddr |= ((unsigned)d)<<((5-inaddrpos)*4);
767 mv_staddr |= pg<<14;
768 } else {
769 mv_staddr &= ~(0x000fU<<((5-inaddrpos)*4));
770 mv_staddr |= ((unsigned)d)<<((5-inaddrpos)*4);
773 if (inaddrpos < 5) ++inaddrpos;
774 return 1;
778 if (isMMEditActive() && key->keysym.unicode > 32 && key->keysym.unicode < 127) {
779 int d = digitInBase((char)key->keysym.unicode, 16);
780 if (d >= 0) {
781 const int mmindex = mmeditpos/2;
782 uint8_t v = mv_pagemap[mmindex];
783 if (mmeditpos%2 == 0) {
784 v &= 0x0F;
785 v |= d<<4;
786 } else {
787 v &= 0xF0;
788 v |= d;
790 if (v < zxMaxMemoryBank) {
791 // swap
792 if (mv_pagemap[mmindex] != v) {
793 int swidx = 0;
794 while (swidx < 64 && mv_pagemap[swidx] != v) ++swidx;
795 if (swidx < 64) {
796 uint8_t vtmp = mv_pagemap[swidx];
797 mv_pagemap[swidx] = mv_pagemap[mmindex];
798 mv_pagemap[mmindex] = vtmp;
799 } else {
800 mv_pagemap[mmindex] = v;
803 ++mmeditpos;
804 if (mmeditpos >= 64*2) mmeditpos = 64*2-1;
806 return 1;
810 if (!isAddrEditActive() && !isMMEditActive() &&
811 key->keysym.sym >= SDLK_0 && key->keysym.sym <= SDLK_9)
813 int dig = (int)(key->keysym.sym-SDLK_0);
814 if (evtShiftKeyDown) {
815 if (mv_viewmode == MV_MODE_TEXT) {
816 if (dig < 5) {
817 const int twdta[5] = {MV_MAX_TEXT_WIDTH,32,42,51,64};
818 mv_textwidth = twdta[dig];
821 } else if (evtCtrlKeyDown) {
822 bookmarks[dig] = mv_staddr;
823 } else if (evtAltKeyDown) {
824 if (bookmarks[dig] != BOOKMARK_NONE) mv_staddr = bookmarks[dig];
826 return 1;
829 switch (key->keysym.sym) {
830 case SDLK_ESCAPE:
831 case SDLK_q:
832 case SDLK_x:
833 if (isAddrEditActive()) inaddrpos = -1;
834 else if (isMMEditActive()) mmeditactive = 0;
835 else memviewSetActive(0);
836 return 1;
838 case SDLK_RETURN:
839 if (isMMEditActive()) {
840 // go to the selected physical page
841 mv_staddr = (((mmeditpos/2)+4)<<14);
843 inaddrpos = -1;
844 mmeditactive = 0;
845 return 1;
847 case SDLK_DELETE: case SDLK_KP_PERIOD:
848 if (isAddrEditActive()) {
849 if (inaddrpos < 2) mv_staddr &= 0xffffU;
850 } else if (isMMEditActive()) {
851 if (evtShiftKeyDown) memviewResetMMap(0);
852 else if (evtCtrlKeyDown) memviewResetMMap(1);
853 else if (evtAltKeyDown) {
854 memviewResetMMap(1);
855 mv_pagemap[0] = 2;
856 mv_pagemap[1] = 0;
857 mv_pagemap[2] = 1;
858 } else if (!evtShiftKeyDown && !evtCtrlKeyDown && !evtAltKeyDown) {
859 const int mmindex = mmeditpos/2;
860 uint8_t v = (uint8_t)mmindex;
861 if (mv_pagemap[mmindex] != v) {
862 int swidx = 0;
863 while (swidx < 64 && mv_pagemap[swidx] != v) ++swidx;
864 if (swidx < 64) {
865 uint8_t vtmp = mv_pagemap[swidx];
866 mv_pagemap[swidx] = mv_pagemap[mmindex];
867 mv_pagemap[mmindex] = vtmp;
868 } else {
869 mv_pagemap[mmindex] = v;
874 return 1;
876 case SDLK_F6:
877 mv_viewmode = !mv_viewmode; // we have only two for now
878 return 1;
880 case SDLK_LEFT: case SDLK_KP4: doLeft(); return 1;
881 case SDLK_RIGHT: case SDLK_KP6: doRight(); return 1;
882 case SDLK_UP: case SDLK_KP8: doUp(); return 1;
883 case SDLK_DOWN: case SDLK_KP2: doDown(); return 1;
884 case SDLK_PAGEUP: case SDLK_KP9: case SDLK_o: doPageUp(); return 1;
885 case SDLK_PAGEDOWN: case SDLK_KP3: case SDLK_p: doPageDown(); return 1;
886 case SDLK_BACKSPACE: if (isAddrEditActive()) doLeft(); return 1;
888 case SDLK_SPACE:
889 if (isAddrEditActive()) return 1;
890 if (mv_viewmode == MV_MODE_TEXT) {
891 if (evtShiftKeyDown) doPageUp(); else doPageDown();
893 return 1;
895 case SDLK_HOME: case SDLK_KP7:
896 if (isAddrEditActive()) inaddrpos = (inaddrpos > 2 ? 2 : 0);
897 return 1;
898 case SDLK_END: case SDLK_KP1:
899 if (isAddrEditActive()) inaddrpos = (inaddrpos < 2 ? 2 : 5);
900 return 1;
902 case SDLK_b:
903 if (!isAddrEditActive() && !isMMEditActive()) {
904 if (evtAltKeyDown) {
905 vt_smm_init(&wkmenu, "Text brightness", (const char *[]){"off", "on", NULL});
906 menu_sel_cb = &menu_set_text_bright;
907 wkmenu.cursor_y = (mv_textcolor >= 0x08 ? 1 : 0);
908 current_menu = &wkmenu;
909 } else {
910 mv_textcolor ^= 0x08;
913 return 1;
915 case SDLK_c:
916 if (!isAddrEditActive() && !isMMEditActive()) {
917 if (evtAltKeyDown) {
918 vt_smm_init(&wkmenu, "Text color", (const char *[]){
919 "blue", "red", "magenta", "green", "cyan", "yellow", "white",
920 NULL});
921 menu_sel_cb = &menu_set_text_color;
922 wkmenu.cursor_y = (mv_textcolor&0x07)-1;
923 current_menu = &wkmenu;
924 } else if (evtCtrlKeyDown && evtShiftKeyDown) {
925 for (unsigned f = 0; f < 10; ++f) bookmarks[f] = BOOKMARK_NONE;
926 } else {
927 mv_textcolor = ((mv_textcolor+1)&0x07)|(mv_textcolor&0x08);
928 if (!mv_textcolor) mv_textcolor = 1;
929 else if (mv_textcolor == 8) mv_textcolor = 9;
932 return 1;
934 case SDLK_e:
935 if (!isMMEditActive() && !isAddrEditActive()) {
936 if (evtAltKeyDown) {
937 vt_smm_init(&wkmenu, "Encoding", (const char *[]){"7 bit", "CP 866", "CP 1251", NULL});
938 menu_sel_cb = &menu_set_encoding;
939 wkmenu.cursor_y = (uint32_t)mv_encoding;
940 current_menu = &wkmenu;
941 } else {
942 memviewSetActive(0);
945 return 1;
947 case SDLK_g:
948 if (!isAddrEditActive() && !isMMEditActive()) inaddrpos = 2;
949 return 1;
951 case SDLK_m:
952 if (!isAddrEditActive() && !isMMEditActive()) {
953 if (evtAltKeyDown) {
954 vt_smm_init(&wkmenu, "View mode", (const char *[]){"hex", "text", NULL});
955 menu_sel_cb = &menu_set_view_mode;
956 wkmenu.cursor_y = (uint32_t)mv_viewmode;
957 current_menu = &wkmenu;
960 return 1;
962 case SDLK_r:
963 if (isMMEditActive() && evtCtrlKeyDown) {
964 rescanPages();
966 return 1;
968 case SDLK_w:
969 if (!isAddrEditActive() && !isMMEditActive()) {
970 if (evtAltKeyDown && mv_viewmode == MV_MODE_TEXT) {
971 vt_smm_init(&wkmenu, "Text width", (const char *[]){"32", "42", "51", "64", "max", NULL});
972 menu_sel_cb = &menu_set_text_width;
973 if (mv_textwidth <= 32) wkmenu.cursor_y = 0;
974 else if (mv_textwidth <= 42) wkmenu.cursor_y = 1;
975 else if (mv_textwidth <= 51) wkmenu.cursor_y = 2;
976 else if (mv_textwidth <= 64) wkmenu.cursor_y = 3;
977 else wkmenu.cursor_y = 4;
978 current_menu = &wkmenu;
981 return 1;
983 case SDLK_F7:
984 if (isAddrEditActive()) return 1;
985 if (!isMMEditActive()) mmeditactive = 1;
986 return 1;
988 case SDLK_F9:
989 if (!isAddrEditActive() && !isMMEditActive()) {
990 if (evtAltKeyDown) {
991 int v = optMaxSpeed;
992 emuSetMaxSpeed(!v);
995 return 1;
997 case SDLK_BACKQUOTE:
998 conVisible = 1;
999 return 1;
1001 case SDLK_F1:
1002 if (!isAddrEditActive() && !isMMEditActive()) {
1003 if (evtAltKeyDown) {
1004 int v = ((optPaused&PAUSE_PERMANENT_MASK) == 0);
1005 emuSetPaused(v ? PAUSE_PERMANENT_SET : PAUSE_PERMANENT_RESET);
1006 return 1;
1009 /* fallthrough */
1010 case SDLK_h:
1011 /*if ((key->keysym.mod&KMOD_ALT) != 0)*/ {
1012 if (isAddrEditActive()) {
1013 vt_smm_init_textview(&wkmenu, "Small help (MM)",
1014 (const char *[]){
1015 "dunno, cursor keys. pgup/pgdn moves by pages.",
1016 "del on page number clears page index.",
1017 NULL
1019 } else if (isMMEditActive()) {
1020 vt_smm_init_textview(&wkmenu, "Small help (AE)",
1021 (const char *[]){
1022 "\x01""C-Del", " restore pages in order",
1023 "\x01""S-Del", " restore pages in 502 order",
1024 "\x01""M-Del", " restore pages in 201 order",
1025 "\x01""Del", " make current page ordered",
1026 "\x01""Enter", " go to the selected page",
1027 "\x01""C-R", " analyze pages, mark pages filled with zeroes as unused",
1028 NULL
1030 } else {
1031 vt_smm_init_textview(&wkmenu, "Small help",
1032 (const char *[]){
1033 "\x01""M-F1", " toggle pause",
1034 "\x01""M-F9", " toggle maxspeed",
1035 "\x01""F6", " switch view mode",
1036 //"\x01""F8", " switch encoding",
1037 "\x01""G", " input address",
1038 "\x01""C", " cycle text colors",
1039 "\x01""B", " toggle text brightness",
1040 "\x01""0-4", " set text width",
1041 "\x01""M-E", " select encoding",
1042 "\x01""M-W", " select text width",
1043 "\x01""M-M", " select view mode",
1044 "\x01""S-<digit>", " maximum text width (in text mode)",
1045 "\x01""C-<digit>", " set bookmark",
1046 "\x01""M-<digit>", " go to bookmark",
1047 "\x01""C-S-C", " clear bookmarks",
1048 NULL
1051 menu_sel_cb = NULL;
1052 current_menu = &wkmenu;
1054 return 1;
1056 default: break;
1059 return 1;