1+ #include " MessageScreen.h"
2+ #include " NavigationStack.h"
3+ #include " PokeScreen.h"
4+ #include " Screen.h"
5+ #include " ../TFT/TFTDisplay.h"
6+ #include " fonts/GillSans_25_vlw.h"
7+ #include " fonts/GillSans_15_vlw.h"
8+ #include " ../Emulator/spectrum.h"
9+ #include " ../Emulator/snaps.h"
10+
11+ #include < sstream>
12+
13+ #define CURSOR_COLOR TFT_CYAN
14+
15+ void PokeScreen::pressKey (SpecKeys key)
16+ {
17+ std::string ctrl = " " ;
18+ size_t l = 0 ;
19+ int16_t addr = 0 ;
20+ std::vector<byte> bytes;
21+ int base = 10 ;
22+
23+ switch (key)
24+ {
25+ case JOYK_FIRE:
26+ case SPECKEY_ENTER:
27+ if (s_addr.length ()<3 || s_data.length ()==0 ){
28+ playErrorBeep ();
29+ return ;
30+ }
31+ addr = get_num (s_addr, 10 );
32+ base = (s_addr[1 ]==' x' ) ? 16 : 10 ;
33+ bytes = get_bytes (s_data, base);
34+ for (int i=0 ; i<bytes.size (); i++){
35+ machine->z80_poke (addr+i, bytes[i]);
36+ }
37+ updateDisplay ();
38+ break ;
39+ case SPECKEY_H:
40+ {
41+ m_navigationStack->push (new MessageScreen (" HELP" , {
42+ " Enter the address and the data to be poked." ,
43+ " If Addr starts with 0x values are hex, else decimal." ,
44+ " Data is space separated." ,
45+ " " ,
46+ " T - switch btween Addr and Data" ,
47+ " Z/Delete - Delete character" ,
48+ " Enter - poke data" ,
49+ " Q/Break - Quit" ,
50+ }, m_tft, m_hdmiDisplay, m_audioOutput));
51+ break ;
52+ }
53+ case SPECKEY_Z:
54+ case SPECKEY_DEL:
55+ ctrl = getControlString ();
56+ l = ctrl.length ();
57+ if (l == 0 ){
58+ playErrorBeep ();
59+ return ;
60+ }
61+ ctrl.pop_back ();
62+ setControlString (ctrl);
63+ updateDisplay ();
64+ break ;
65+ case SPECKEY_T:
66+ active_control = 1 - active_control;
67+ updateDisplay ();
68+ break ;
69+ case SPECKEY_Q:
70+ case SPECKEY_BREAK:
71+ m_navigationStack->pop ();
72+ return ;
73+ default :
74+ if (specKeyToLetter.find (key) == specKeyToLetter.end () && key != SPECKEY_SPACE)
75+ break ;
76+ char chr = (key==SPECKEY_SPACE) ? ' ' : specKeyToLetter.at (key);
77+ if (chr == ' X' ){
78+ bool bad = false ;
79+ if (active_control == 1 ){
80+ // Only needs to be used for address. Bytes will be the same base
81+ bad = true ;
82+ } else if (s_addr.length ()!=1 || s_addr[0 ] != ' 0' ){
83+ // Can only use after a 0 in the first position
84+ bad = true ;
85+ }
86+ if (bad){
87+ playErrorBeep ();
88+ return ;
89+ }
90+ // Convert to lowercase
91+ s_addr.push_back (' x' );
92+ updateDisplay ();
93+ return ;
94+ }
95+ if (chr == ' ' ){
96+ // Can only be used to separate data
97+ if (active_control == 0 ){
98+ playErrorBeep ();
99+ return ;
100+ }
101+ s_data.push_back (chr);
102+ updateDisplay ();
103+ return ;
104+ }
105+ if (isHexadecimalDigit (chr)) {
106+ ctrl = getControlString ();
107+ ctrl.push_back (chr);
108+ setControlString (ctrl);
109+ playKeyClick ();
110+ updateDisplay ();
111+ return ;
112+ }
113+ }
114+ };
115+
116+ void PokeScreen::updateDisplay ()
117+ {
118+ m_tft.loadFont (GillSans_15_vlw);
119+ m_tft.startWrite ();
120+ m_tft.fillRect (0 , 0 , m_tft.width (), m_tft.height (), TFT_BLACK);
121+ m_tft.drawRect (0 , 0 , m_tft.width (), m_tft.height (), TFT_WHITE);
122+ m_tft.setTextColor (TFT_CYAN, TFT_BLACK);
123+ m_tft.drawString (" Poke Screen (H for help)" , 2 , 2 );
124+
125+ m_tft.setTextColor (TFT_WHITE, TFT_BLACK);
126+ const char * c_addr = " Addr:" ;
127+ auto textSize = m_tft.measureString (c_addr);
128+ const int16_t edit_y = textSize.x + 7 ;
129+ int16_t cursor_x;
130+
131+ m_tft.drawString (c_addr, 2 , 20 );
132+ cursor_x = m_tft.drawStringAndMeasure (s_addr.c_str (), edit_y, 20 );
133+ if (active_control == 0 ){
134+ m_tft.fillRect (cursor_x, 20 , 2 , textSize.y , CURSOR_COLOR);
135+ }
136+
137+ m_tft.drawString (" Data:" , 2 , 40 );
138+ cursor_x = m_tft.drawStringAndMeasure (s_data.c_str (), edit_y, 40 );
139+ if (active_control == 1 ){
140+ m_tft.fillRect (cursor_x, 40 , 2 , textSize.y , CURSOR_COLOR);
141+ }
142+
143+ uint16_t addr;
144+ if (s_addr.length ()<3 )
145+ addr = 0 ;
146+ else
147+ addr = get_num (s_addr, 10 );
148+
149+ char buf[16 ];
150+ bool is_hex = s_addr[1 ] == ' x' ;
151+ std::vector<byte> pokes;
152+ if (s_data.length ()){
153+ // Base doesn't really matter here - doing this just to count the bytes
154+ pokes = get_bytes (s_data, 16 );
155+ }
156+
157+ uint16_t poke_len = pokes.size ();
158+ if (poke_len == 0 ){
159+ poke_len = 1 ;
160+ }
161+ uint16_t start_addr = addr - 4 ;
162+ uint16_t end_addr = addr + poke_len + 4 ;
163+
164+ m_tft.setTextColor (TFT_YELLOW, TFT_BLACK);
165+ cursor_x = m_tft.drawStringAndMeasure (" Bytes at " , 2 , 60 );
166+ if (is_hex)
167+ sprintf (buf, " %04X" , addr);
168+ else
169+ sprintf (buf, " %d" , addr);
170+ cursor_x = m_tft.drawStringAndMeasure (buf, cursor_x, 60 );
171+ cursor_x = 2 ;
172+
173+ for (int i=start_addr; i<end_addr; i++){
174+ auto b = machine->z80_peek (i);
175+ if (is_hex)
176+ sprintf (buf, " %02X" , b);
177+ else
178+ sprintf (buf, " %d" , b);
179+ if (i<addr || i>=addr+poke_len)
180+ m_tft.setTextColor (TFT_WHITE, TFT_BLACK);
181+ else
182+ m_tft.setTextColor (TFT_YELLOW, TFT_BLACK);
183+ cursor_x = m_tft.drawStringAndMeasure (buf, cursor_x, 80 ) + 5 ;
184+ }
185+
186+ m_tft.endWrite ();
187+ }
188+
189+ std::vector<std::string> PokeScreen::split (const std::string &s, char delim){
190+ std::vector<std::string> result;
191+ std::stringstream ss (s);
192+ std::string item;
193+
194+ while (getline (ss, item, delim)){
195+ result.push_back (item);
196+ }
197+ return result;
198+ }
199+
200+ // Parse a string as a number with the specified base.
201+ // If the string starts with 0x the base is ignored and the
202+ // string is parsed as hex
203+ int16_t PokeScreen::get_num (const std::string &s, int base){
204+ if (s[0 ] == ' 0' && s[1 ] == ' x' ){
205+ base = 16 ;
206+ }
207+ return std::stoi (s, nullptr , base);
208+ }
209+
210+ // Parse a string of bytes delimited by single spaces
211+ std::vector<byte> PokeScreen::get_bytes (const std::string &s, int base){
212+ std::vector<byte> result;
213+ auto s_bytes = split (s, ' ' );
214+
215+ for (const std::string& s_byte: s_bytes){
216+ if (s_byte.length ()==0 )
217+ continue ;
218+ auto b = get_num (s_byte, base) & 0xFF ;
219+ result.push_back (b);
220+ }
221+ return result;
222+ }
223+
224+ std::string PokeScreen::getControlString (){
225+ switch (active_control){
226+ case 0 :
227+ return s_addr;
228+ default :
229+ return s_data;
230+ }
231+ }
232+
233+ void PokeScreen::setControlString (std::string str){
234+ switch (active_control){
235+ case 0 :
236+ s_addr = str;
237+ break ;
238+ default :
239+ s_data = str;
240+ break ;
241+ }
242+ }
0 commit comments