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"
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;
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);*/
65 //K8ASSERT(ae-as == 8);
67 if (isBeamInScreenDollar(x
, y
)) {
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);
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
);
82 // copy this to alternate screen for slow speeds
84 while (asStart
< as
) {
85 zxScreen
[zxScreenCurrent
^1][asStart
] = zxScreen
[zxScreenCurrent
][asStart
];
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);
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
126 // now update screen from (curx, cury) to (ex, ey); note that (ex, ey) is not included in updated region
127 K8ASSERT(cury
<= ey
);
130 for (; curx
< 42; ++curx
) zxUpdateScreenAtBeamXY(curx
, cury
, 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
;
140 while (zxOldScreenTS
<= curts
+1) {
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
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
) {
162 } else if (ts
>= zxLineStartTS
[192+24*2]+zxLateTimings
) {
167 getBeamXY(ts
, &x
, &y
, &ofs
);
169 int res
= beamXY2Addr(x
, y
);
170 return (res
>= 0 ? res
: 400*352);