emu: remember page editor cursor position in memview
[zymosis.git] / src / ZXEmuT / zxscrdraw.c
blob1dbca54b6c76a3a610be28d02d95c123220b65e2
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 #include "libvideo/video.h"
21 #include "zxscrdraw.h"
22 #include "emucommon.h"
23 #include "emuvars.h"
26 static inline void getBeamXY (int tstates, int *x, int *y, int *ofs) {
27 tstates += zxLateTimings;
28 if (tstates < zxLineStartTS[0]) { *x = *y = -1; return; }
29 *y = (tstates-zxLineStartTS[0])/machineInfo.tsperline;
30 if (*y >= 0 && *y <= 192+24*2) {
31 *x = (tstates-zxLineStartTS[*y])/4;
32 if (ofs) *ofs = (tstates-zxLineStartTS[*y])%4;
33 } else {
34 *x = 0;
35 if (ofs) *ofs = 0;
40 static inline int beamXY2Addr (int x, int y) {
41 return (x >= 0 && y >= 0 && x <= 42 && y <= 192+24*2 ? (48-3*8+y)*352+((48-4*8)+x*8) : -1);
45 static inline int isBeamInScreenDollar (int x, int y) {
46 return (y >= 24 && y < 24+192 && x >= 4 && x < 4+32);
50 // returns current beam position, 4ts offset, and "in screen$" flag
51 // result is "in screen$"
52 int zxScrGetBeamInfo (int tstates, int *x, int *y, int *ofs) {
53 getBeamXY(tstates, x, y, ofs);
54 return isBeamInScreenDollar(*x, *y);
58 static inline void zxUpdateScreenAtBeamXY (int x, int y, int ofs) {
59 if (y >= 0 && y < 192+24*2 && x >= 0 && x <= 41) {
60 int as = beamXY2Addr(x, y); /*, ae = beamXY2Addr(x+1, y);*/
62 //K8ASSERT(as >= 0);
63 //K8ASSERT(ae >= 0);
64 //K8ASSERT(as < ae);
65 //K8ASSERT(ae-as == 8);
67 if (isBeamInScreenDollar(x, y)) {
68 // screen
69 int sx = x-4, sy = y-24; // screen position
70 uint16_t addr = zxScrLineOfs[sy]+sx, zus = zxUlaSnow[sy][sx];
71 uint8_t bt = (zxWasUlaSnow+zus > 1 ? zus-1 : zxScreenBank[addr]);
72 uint8_t attr = zxScreenBank[32*192+sy/8*32+sx];
73 uint8_t paper = (attr>>3)&0x0f, ink = (attr&0x07)|(paper&0x08);
74 int asStart = as;
75 //if (zxWasUlaSnow+zus > 1) bt = zus-1;
76 if (attr&0x80 && zxFlashState) { const uint8_t t = paper; paper = ink; ink = t; }
77 //if (vidColorMode == 2) { ink |= 8; paper |= 8; }
78 for (unsigned f = 0; f < 8; ++f) {
79 zxScreen[zxScreenCurrent][as++] = (bt&0x80 ? ink : paper);
80 bt = (bt&0x7f)<<1;
82 // copy this to alternate screen for slow speeds
83 if (optSpeed < 100) {
84 while (asStart < as) {
85 zxScreen[zxScreenCurrent^1][asStart] = zxScreen[zxScreenCurrent][asStart];
86 ++asStart;
89 } else {
90 // border
91 if (machineInfo.genuine) {
92 memset(zxScreen[zxScreenCurrent]+as, zxBorder, 8);
93 // copy this to alternate screen for slow speeds
94 if (optSpeed < 100) memset(zxScreen[zxScreenCurrent^1]+as, zxBorder, 8);
95 } else {
96 memset(zxScreen[zxScreenCurrent]+as+ofs*2, zxBorder, 2);
97 // copy this to alternate screen for slow speeds
98 if (optSpeed < 100) memset(zxScreen[zxScreenCurrent^1]+as+ofs*2, zxBorder, 2);
105 // draw screen from `zxOldScreenTS` to `curts-1`
106 void zxRealiseScreen (int curts) {
107 if (!optNoScreenReal) {
108 int curx, cury, ex, ey;
110 if (curts < zxOldScreenTS || curts < zxLineStartTS[0]+zxLateTimings ||
111 zxOldScreenTS >= zxLineStartTS[192+24*2]+zxLateTimings) return; // do nothing
112 if (curts >= zxLineStartTS[192+24*2]+zxLateTimings) curts = zxLineStartTS[192+24*2]+zxLateTimings;
114 if (zxOldScreenTS < zxLineStartTS[0]+zxLateTimings) zxOldScreenTS = zxLineStartTS[0]+zxLateTimings;
116 if (machineInfo.genuine) {
117 getBeamXY(zxOldScreenTS, &curx, &cury, NULL);
118 getBeamXY(curts, &ex, &ey, NULL);
119 // ULA reads 4 bytes per update (2 bytes of bitmap and 2 bytes of attrs),
120 // so let's emulate that
121 if (machineInfo.genuine && isBeamInScreenDollar(ex, ey) && !(ex&0x01)) {
122 // i like to move it, move it
123 // ex it NOT included, hence the '!' above
124 ++ex;
126 // now update screen from (curx, cury) to (ex, ey); note that (ex, ey) is not included in updated region
127 K8ASSERT(cury <= ey);
128 // full lines
129 while (cury < ey) {
130 for (; curx < 42; ++curx) zxUpdateScreenAtBeamXY(curx, cury, 0);
131 ++cury;
132 curx = 0;
135 for (; curx < ex; ++curx) zxUpdateScreenAtBeamXY(curx, cury, 0);
137 zxOldScreenTS = zxLineStartTS[ey]+ex*4+zxLateTimings; // will start from here
138 if (zxOldScreenTS >= zxLineStartTS[192+24*2]+zxLateTimings) zxOldScreenTS = machineInfo.tsperframe;
139 } else {
140 while (zxOldScreenTS <= curts+1) {
141 int ofs = 0;
143 getBeamXY(zxOldScreenTS, &curx, &cury, &ofs);
144 zxUpdateScreenAtBeamXY(curx, cury, ofs);
145 if (ofs == 0 && isBeamInScreenDollar(curx, cury)) {
146 zxOldScreenTS += 4; // it's ok to skip the screen in this way
147 } else {
148 ++zxOldScreenTS;
151 if (zxOldScreenTS >= zxLineStartTS[192+24*2]+zxLateTimings) zxOldScreenTS = machineInfo.tsperframe;
157 // "shaded" means "not yet touched by the beam"
158 int zxScreenLineShadeStart (int ts) {
159 if (ts < zxLineStartTS[0]+zxLateTimings) {
160 // before screen
161 return 0;
162 } else if (ts >= zxLineStartTS[192+24*2]+zxLateTimings) {
163 // after screen
164 return 400*352;
165 } else {
166 int x, y, ofs;
167 getBeamXY(ts, &x, &y, &ofs);
168 if (x > 42) x = 42;
169 int res = beamXY2Addr(x, y);
170 return (res >= 0 ? res : 400*352);