├── .editorconfig
├── .gitignore
├── Bridge.cs
├── CPU.cs
├── ELFSharp.dll
├── GDBServer.cs
├── LICENSE
├── Log.cs
├── NOTPSXSERIAL.CS
├── PCDrv.cs
├── README.md
├── README.txt
├── SerialTarget.cs
├── TCPTarget.cs
├── TargetDataPort.cs
├── TransferLogic.cs
├── Utils.cs
├── app.config
├── buildme.bat
├── buildnops.bat
├── buildnops.sh
├── elfsharp_license.txt
├── macbuild.sh
├── macrun.sh
├── nops
├── nops.exe
├── nops.exe.config
├── nops.ico
├── nops_icon_PSD.psd
├── nops_proj.csproj
├── nops_proj.csproj.user
├── nops_sln.sln
├── psx_helloworld.exe
├── social_card_PNG.png
└── social_card_PSD.psd
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
2 | root = true
3 |
4 | # C# files
5 | [*.cs]
6 |
7 | #### Core EditorConfig Options ####
8 |
9 | # Indentation and spacing
10 | indent_size = 4
11 | indent_style = space
12 | tab_width = 4
13 |
14 | # New line preferences
15 | end_of_line = crlf
16 | insert_final_newline = false
17 |
18 | #### .NET Coding Conventions ####
19 |
20 | # Organize usings
21 | dotnet_separate_import_directive_groups = false
22 | dotnet_sort_system_directives_first = false
23 | file_header_template = unset
24 |
25 | # this. and Me. preferences
26 | dotnet_style_qualification_for_event = false
27 | dotnet_style_qualification_for_field = false
28 | dotnet_style_qualification_for_method = false
29 | dotnet_style_qualification_for_property = false
30 |
31 | # Language keywords vs BCL types preferences
32 | dotnet_style_predefined_type_for_locals_parameters_members = true
33 | dotnet_style_predefined_type_for_member_access = true
34 |
35 | # Parentheses preferences
36 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
37 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
38 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary
39 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
40 |
41 | # Modifier preferences
42 | dotnet_style_require_accessibility_modifiers = for_non_interface_members
43 |
44 | # Expression-level preferences
45 | dotnet_style_coalesce_expression = true
46 | dotnet_style_collection_initializer = true
47 | dotnet_style_explicit_tuple_names = true
48 | dotnet_style_namespace_match_folder = true
49 | dotnet_style_null_propagation = true
50 | dotnet_style_object_initializer = true
51 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
52 | dotnet_style_prefer_auto_properties = true
53 | dotnet_style_prefer_compound_assignment = true
54 | dotnet_style_prefer_conditional_expression_over_assignment = true
55 | dotnet_style_prefer_conditional_expression_over_return = true
56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
57 | dotnet_style_prefer_inferred_tuple_names = true
58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
59 | dotnet_style_prefer_simplified_boolean_expressions = true
60 | dotnet_style_prefer_simplified_interpolation = true
61 |
62 | # Field preferences
63 | dotnet_style_readonly_field = true
64 |
65 | # Parameter preferences
66 | dotnet_code_quality_unused_parameters = all
67 |
68 | # Suppression preferences
69 | dotnet_remove_unnecessary_suppression_exclusions = none
70 |
71 | # New line preferences
72 | dotnet_style_allow_multiple_blank_lines_experimental = true
73 | dotnet_style_allow_statement_immediately_after_block_experimental = true
74 |
75 | #### C# Coding Conventions ####
76 |
77 | # var preferences
78 | csharp_style_var_elsewhere = false
79 | csharp_style_var_for_built_in_types = false
80 | csharp_style_var_when_type_is_apparent = false
81 |
82 | # Expression-bodied members
83 | csharp_style_expression_bodied_accessors = true
84 | csharp_style_expression_bodied_constructors = false
85 | csharp_style_expression_bodied_indexers = true
86 | csharp_style_expression_bodied_lambdas = true
87 | csharp_style_expression_bodied_local_functions = false
88 | csharp_style_expression_bodied_methods = false
89 | csharp_style_expression_bodied_operators = false
90 | csharp_style_expression_bodied_properties = true
91 |
92 | # Pattern matching preferences
93 | csharp_style_pattern_matching_over_as_with_null_check = true
94 | csharp_style_pattern_matching_over_is_with_cast_check = true
95 | csharp_style_prefer_not_pattern = true
96 | csharp_style_prefer_pattern_matching = true
97 | csharp_style_prefer_switch_expression = true
98 |
99 | # Null-checking preferences
100 | csharp_style_conditional_delegate_call = true
101 |
102 | # Modifier preferences
103 | csharp_prefer_static_local_function = true
104 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
105 |
106 | # Code-block preferences
107 | csharp_prefer_braces = true
108 | csharp_prefer_simple_using_statement = true
109 |
110 | # Expression-level preferences
111 | csharp_prefer_simple_default_expression = true
112 | csharp_style_deconstructed_variable_declaration = true
113 | csharp_style_implicit_object_creation_when_type_is_apparent = true
114 | csharp_style_inlined_variable_declaration = true
115 | csharp_style_pattern_local_over_anonymous_function = true
116 | csharp_style_prefer_index_operator = true
117 | csharp_style_prefer_range_operator = true
118 | csharp_style_throw_expression = true
119 | csharp_style_unused_value_assignment_preference = discard_variable
120 | csharp_style_unused_value_expression_statement_preference = discard_variable
121 |
122 | # 'using' directive preferences
123 | csharp_using_directive_placement = outside_namespace
124 |
125 | # New line preferences
126 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
127 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
128 | csharp_style_allow_embedded_statements_on_same_line_experimental = true
129 |
130 | #### C# Formatting Rules ####
131 |
132 | # New line preferences
133 | csharp_new_line_before_catch = false
134 | csharp_new_line_before_else = false
135 | csharp_new_line_before_finally = false
136 | csharp_new_line_before_members_in_anonymous_types = false
137 | csharp_new_line_before_members_in_object_initializers = false
138 | csharp_new_line_before_open_brace = none
139 | csharp_new_line_between_query_expression_clauses = false
140 |
141 | # Indentation preferences
142 | csharp_indent_block_contents = true
143 | csharp_indent_braces = false
144 | csharp_indent_case_contents = true
145 | csharp_indent_case_contents_when_block = true
146 | csharp_indent_labels = no_change
147 | csharp_indent_switch_labels = true
148 |
149 | # Space preferences
150 | csharp_space_after_cast = false
151 | csharp_space_after_colon_in_inheritance_clause = true
152 | csharp_space_after_comma = true
153 | csharp_space_after_dot = false
154 | csharp_space_after_keywords_in_control_flow_statements = true
155 | csharp_space_after_semicolon_in_for_statement = true
156 | csharp_space_around_binary_operators = before_and_after
157 | csharp_space_around_declaration_statements = false
158 | csharp_space_before_colon_in_inheritance_clause = true
159 | csharp_space_before_comma = false
160 | csharp_space_before_dot = false
161 | csharp_space_before_open_square_brackets = false
162 | csharp_space_before_semicolon_in_for_statement = false
163 | csharp_space_between_empty_square_brackets = false
164 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
165 | csharp_space_between_method_call_name_and_opening_parenthesis = false
166 | csharp_space_between_method_call_parameter_list_parentheses = true
167 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
168 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
169 | csharp_space_between_method_declaration_parameter_list_parentheses = true
170 | csharp_space_between_parentheses = control_flow_statements
171 | csharp_space_between_square_brackets = true
172 |
173 | # Wrapping preferences
174 | csharp_preserve_single_line_blocks = true
175 | csharp_preserve_single_line_statements = true
176 |
177 | #### Naming styles ####
178 |
179 | # Naming rules
180 |
181 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
182 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
183 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
184 |
185 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
186 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
187 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
188 |
189 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
190 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
191 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
192 |
193 | # Symbol specifications
194 |
195 | dotnet_naming_symbols.interface.applicable_kinds = interface
196 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
197 | dotnet_naming_symbols.interface.required_modifiers =
198 |
199 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
200 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
201 | dotnet_naming_symbols.types.required_modifiers =
202 |
203 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
204 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
205 | dotnet_naming_symbols.non_field_members.required_modifiers =
206 |
207 | # Naming styles
208 |
209 | dotnet_naming_style.pascal_case.required_prefix =
210 | dotnet_naming_style.pascal_case.required_suffix =
211 | dotnet_naming_style.pascal_case.word_separator =
212 | dotnet_naming_style.pascal_case.capitalization = pascal_case
213 |
214 | dotnet_naming_style.begins_with_i.required_prefix = I
215 | dotnet_naming_style.begins_with_i.required_suffix =
216 | dotnet_naming_style.begins_with_i.word_separator =
217 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
218 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | .vs
5 | .idea
6 | .vscode
7 |
8 | Thumbs.db
9 | .DS_Store
10 | desktop.ini
11 |
12 | *.bin*
13 | *.log*
14 | *exclude*
15 |
16 | *.psx
17 | *.mcr
18 | *.elf
19 |
20 | test_*
21 | comport.txt
22 |
23 | bin\
24 | obj\
25 |
26 | bin
27 | obj
28 | *.pdb
29 |
30 | oldnops.exe
31 | testdump.cmd
32 | testexe.cmd
33 |
--------------------------------------------------------------------------------
/Bridge.cs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | //
6 | // GDB TCP->SIO bridge for Unirom 8
7 | //
8 | // NOTE!
9 | //
10 | // While all of the basic debug functionality is present, the GDB
11 | // bridge is still very much a work in progress!
12 | //
13 |
14 | using System;
15 | using System.Text;
16 | using System.IO.Ports;
17 | using System.Net;
18 | using System.Net.Sockets;
19 | using System.Threading;
20 |
21 | public enum MonitorMode {
22 | // serial is monitored for printf, pcdrv, halt events, etc
23 | MONITOR_OR_PCDRV,
24 | // as MONITOR_OR_PCDRV, but also forwarded to a socket
25 | SERIALBRIDGE,
26 | // as MONITOR_OR_PCDRV, but for GDB
27 | GDB
28 | };
29 |
30 | ///
31 | /// Monitor data over the target connection and optionally
32 | /// listens on a socket in bridge mode or gdb mode
33 | ///
34 | public class Bridge {
35 |
36 | // TODO: could do to document some of these a bit better.
37 |
38 | public static bool enabled = false;
39 |
40 | public static TargetDataPort serial => Program.activeSerial;
41 |
42 | public static Socket socket;
43 |
44 | // default monitor mode, no external socket/gdb
45 | public static MonitorMode activeBridgeMode = MonitorMode.MONITOR_OR_PCDRV;
46 |
47 | public const int socketBufferSize = 512;
48 | public static byte[] socketBuffer = new byte[ socketBufferSize ];
49 | public static StringBuilder sb = new StringBuilder();
50 | public static string socketString; // oh boy, this will be nuts on the GC
51 |
52 | public static Socket replySocket;
53 | private static IPEndPoint localEndpoint;
54 |
55 | public static void Init( MonitorMode inMode, UInt32 localPort, string localIP = "" ) {
56 |
57 | activeBridgeMode = inMode;
58 | InitListenServer( localPort, localIP );
59 |
60 | if ( inMode == MonitorMode.GDB ) {
61 | GDBServer.Init();
62 | Log.WriteLine( $"Monitoring psx and accepting GDB connections on {localIP}:{localPort}" );
63 | }
64 |
65 | // Shared function
66 | MonitorSerial();
67 |
68 | }
69 |
70 | // Called when a connection has been accepted
71 | public static void AcceptCallback( IAsyncResult result ) {
72 |
73 | Socket whichSocket = (Socket)result.AsyncState;
74 | Socket newSocket = socket.EndAccept( result );
75 |
76 | Log.WriteLine( "Remote connection to local socket accepted: " + newSocket.LocalEndPoint );
77 |
78 | replySocket = newSocket;
79 |
80 | // on the new socket or it'll moan. I don't like this setup.
81 | newSocket.BeginReceive( socketBuffer, 0, socketBufferSize, 0, new AsyncCallback( ReceiveCallback ), newSocket );
82 |
83 | }
84 |
85 | ///
86 | /// Received data over a socket:
87 | /// - in bridge mode
88 | /// - from GDB
89 | ///
90 | /// A
91 | private static void ReceiveCallback( IAsyncResult ar ) {
92 |
93 | //Console.WriteLine( "SOCKET: RCB " + ar.AsyncState );
94 |
95 | Socket recvSocket = (Socket)ar.AsyncState;
96 |
97 | //Console.WriteLine( "SOCKET RCB 1 " + recvSocket );
98 |
99 | int numBytesRead = recvSocket.EndReceive( ar, out SocketError errorCode );
100 |
101 | if( errorCode != SocketError.Success ) {
102 | if(errorCode == SocketError.ConnectionReset ) {
103 | Log.WriteLine( "Remote connection closed, restarting listen server", LogType.Warning );
104 | Log.WriteLine( "CTRL-C to exit" );
105 | RestartListenServer( );
106 | }
107 | Log.WriteLine( "errorCode: " + errorCode.ToString(), LogType.Debug );
108 | return;
109 | }
110 |
111 | //Console.WriteLine( "SOCKET RCB 2 " + numBytesRead );
112 |
113 | if ( numBytesRead > 0 ) {
114 |
115 | // copy the bytes (from the buffer specificed in BeginRecieve), into the stringbuilder
116 | string thisPacket = ASCIIEncoding.ASCII.GetString( socketBuffer, 0, numBytesRead );
117 | sb.Append( thisPacket );
118 |
119 | socketString = sb.ToString();
120 | //Console.WriteLine( "\rSOCKET: rec: " + thisPacket );
121 |
122 | if ( socketString.IndexOf( "" ) > -1 ) {
123 | //Console.WriteLine( "Read {0} bytes, done!: {1}", numBytesRead, socketString );
124 |
125 | } else {
126 |
127 |
128 | if ( activeBridgeMode == MonitorMode.GDB ) {
129 |
130 | GDBServer.ProcessData( socketString );
131 | sb.Clear();
132 |
133 | } else
134 | if ( activeBridgeMode == MonitorMode.SERIALBRIDGE ) {
135 |
136 | // To echo it back:
137 | //Send( recvSocket, thisPacket );
138 |
139 | // Send the incoming socket data over sio
140 | TransferLogic.activeSerial.Write( socketBuffer, 0, numBytesRead );
141 |
142 | }
143 |
144 | //Console.WriteLine( "SOCKET: Grabbing more data" );
145 |
146 | try {
147 | recvSocket.BeginReceive( socketBuffer, 0, socketBufferSize, 0, new AsyncCallback( ReceiveCallback ), recvSocket );
148 | } catch ( Exception ex ) {
149 | Log.WriteLine( "SOCKET: RCB EXCEPTION: " + ex.Message, LogType.Error );
150 | }
151 |
152 | }
153 |
154 |
155 | } else {
156 | //Console.WriteLine( "Read 0 bytes" );
157 | RestartListenServer( );
158 | }
159 |
160 | }
161 |
162 | public static void RestartListenServer( ) {
163 | Log.WriteLine( "Restarting listen server" );
164 | socket.Close();
165 | StartListenServer( );
166 |
167 | if ( activeBridgeMode == MonitorMode.GDB ) {
168 | GDBServer.ResetConnection();
169 | }
170 | }
171 |
172 | public static void StartListenServer( ) {
173 | socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
174 | socket.Bind( localEndpoint );
175 | socket.Listen( 2 );
176 | socket.BeginAccept( new AsyncCallback( AcceptCallback ), socket );
177 | }
178 |
179 | public static void Send( string inData ) {
180 | Send( replySocket, inData );
181 | }
182 |
183 |
184 | public static void Send( Socket inSocket, string inData ) {
185 |
186 | // TODO: we could probs check if there's
187 | // any sort of connection long before it
188 | // reaches this point.
189 | if ( inSocket == null ) {
190 | return;
191 | }
192 |
193 | byte[] bytes = ASCIIEncoding.ASCII.GetBytes( inData );
194 |
195 | inSocket.BeginSend( bytes, 0, bytes.Length, 0, out SocketError errorCode, new AsyncCallback( SendCallback ), inSocket );
196 |
197 | if ( errorCode != SocketError.Success ) {
198 | if ( errorCode == SocketError.ConnectionReset ) {
199 | Log.WriteLine( "Error sending, restarting listen server", LogType.Warning );
200 | Log.WriteLine( "CTRL-C to exit" );
201 | RestartListenServer();
202 | }
203 | Log.WriteLine( "errorCode: " + errorCode.ToString(), LogType.Debug );
204 | return;
205 | }
206 |
207 | }
208 |
209 | // Send() succeeded
210 | private static void SendCallback( IAsyncResult ar ) {
211 |
212 | Socket whichSocket = (Socket)ar.AsyncState;
213 |
214 | int bytesSent = whichSocket.EndSend( ar );
215 | }
216 |
217 | //
218 | // Listens on the given IP and port
219 | // Shared by bridge and gdb modes
220 | //
221 | private static void InitListenServer( UInt32 inPort, string localIP = "" ) {
222 |
223 | IPAddress ip;
224 |
225 | if ( string.IsNullOrEmpty( localIP ) ) {
226 | ip = IPAddress.Parse( "127.0.0.1" );
227 | } else {
228 | Log.WriteLine( "Binding IP " + localIP );
229 | ip = IPAddress.Parse( localIP );
230 | }
231 |
232 | Log.WriteLine( "Opening a listen server on " + ip + ":" + inPort );
233 |
234 | localEndpoint = new IPEndPoint( ip, (int)inPort );
235 |
236 | StartListenServer();
237 |
238 | }
239 |
240 |
241 |
242 | ///
243 | /// Monitor serial data from the psx
244 | ///
245 | /// 1: printfs() over serial
246 | /// 2: PCDRV
247 | /// 3: `HALT` (break/exception/etc)
248 | ///
249 | /// + In bridge mode:
250 | /// 4: forwards serial traffic over a TCP socket (because mono hasn't implemented SIO callbacks)
251 | ///
252 | /// + In GDB mode:
253 | /// 5: forwards HALT etc to GDB
254 | ///
255 | ///
256 |
257 | // TODO: move somewhere appropriate
258 | public static void MonitorSerial() {
259 |
260 | const int ESCAPECHAR = 0x00;
261 | // Old mode (unescaped): when this reads HLTD, kdebug has halted the playstation.
262 | char[] last4ResponseChars = new char[] { 'x', 'x', 'x', 'x' };
263 |
264 | // 10kb buffer?
265 | byte[] responseBytes = new byte[ 2048 * 10 ];
266 | int bytesInBuffer = 0;
267 |
268 | bool lastByteWasEscaped = false;
269 |
270 | while ( true ) {
271 |
272 | // Ensure that socket threads aren't trying
273 | // to e.g. read/write memory at the same time
274 | lock( SerialTarget.serialLock ) {
275 |
276 | while ( serial.BytesToRead > 0 && bytesInBuffer < responseBytes.Length ) {
277 |
278 | int thisByte = (byte)serial.ReadByte();
279 | bool thisByteIsEscapeChar = (thisByte == ESCAPECHAR);
280 |
281 | //Console.WriteLine( $"Got val {thisByte.ToString( "X" )} escaped={thisByteIsEscapeChar} lastWasEscapeChar={lastByteWasEscaped}" );
282 |
283 | // The byte before this one was an escape sequence...
284 | if ( lastByteWasEscaped ) {
285 |
286 | // 2x escape cars = just print that char
287 | if ( thisByteIsEscapeChar ) {
288 |
289 | // a properly escaped doublet can go in the buffer.
290 | responseBytes[ bytesInBuffer++ ] = ESCAPECHAR;
291 | Log.Write( ((char)thisByte).ToString(), LogType.Stream );
292 |
293 | } else {
294 |
295 | if ( thisByte == 'p' ) {
296 |
297 | PCDrv.ReadCommand();
298 |
299 | }
300 |
301 | }
302 |
303 | // whether we're printing an escaped char or acting on
304 | // a sequence, reset things back to normal.
305 | lastByteWasEscaped = false;
306 | continue; // next inner loop
307 |
308 | }
309 |
310 | // Any non-escape char: print it, dump it, send it, etc
311 | if ( !thisByteIsEscapeChar ) {
312 |
313 | responseBytes[ bytesInBuffer++ ] = (byte)thisByte;
314 | Log.Write( ((char)thisByte).ToString(), LogType.Stream );
315 |
316 | // TODO: remove this unescaped method after a few versions
317 | // Clunky way to do it, but there's no unboxing or reallocation
318 | last4ResponseChars[ 0 ] = last4ResponseChars[ 1 ];
319 | last4ResponseChars[ 1 ] = last4ResponseChars[ 2 ];
320 | last4ResponseChars[ 2 ] = last4ResponseChars[ 3 ];
321 | last4ResponseChars[ 3 ] = (char)thisByte;
322 | if (
323 | last4ResponseChars[ 0 ] == 'H' && last4ResponseChars[ 1 ] == 'L'
324 | && last4ResponseChars[ 2 ] == 'T' && last4ResponseChars[ 3 ] == 'D'
325 | ) {
326 | Log.WriteLine( "PSX may have halted (<8.0.I)!", LogType.Warning );
327 |
328 | CPU.GetRegs();
329 | CPU.DumpRegs();
330 | if ( CPU.IsStepBreakSet ) {
331 | CPU.StepBreakCallback();
332 | }
333 | GDBServer.SetHaltStateInternal( CPU.HaltState.HALT, true );
334 | }
335 |
336 | }
337 |
338 | lastByteWasEscaped = thisByteIsEscapeChar;
339 |
340 | } // bytestoread > 0
341 |
342 | if ( !GDBServer.IsEnabled ) {
343 | // Send the buffer back in a big chunk if we're not waiting
344 | // on an escaped byte resolving
345 | if ( bytesInBuffer > 0 && !lastByteWasEscaped ) {
346 | // send it baaahk
347 | if ( replySocket != null ) {
348 | replySocket.Send( responseBytes, 0, bytesInBuffer, SocketFlags.None, out SocketError errorCode );
349 | }
350 | bytesInBuffer = 0;
351 | }
352 |
353 | if ( Console.KeyAvailable ) {
354 | ConsoleKeyInfo keyVal = Console.ReadKey( true );
355 | serial.Write( new byte[] { (byte)keyVal.KeyChar }, 0, 1 );
356 | Log.Write( (keyVal.KeyChar).ToString(), LogType.Stream );
357 | }
358 | }
359 |
360 | } // serial lock object
361 |
362 | // Yield the thread
363 | Thread.Sleep( 1 );
364 |
365 | } // while
366 |
367 | }
368 |
369 |
370 | }
371 |
--------------------------------------------------------------------------------
/CPU.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 |
8 | public class CPU {
9 |
10 | public enum HaltState { RUNNING, HALT }; // Keep track of the console's state
11 |
12 | // The PSX registers
13 | public enum GPR {
14 | stat,
15 | badv, // repurposed for unirom
16 |
17 | // GPR
18 | r0, at, v0, v1, a0, a1, a2, a3,
19 | t0, t1, t2, t3, t4, t5, t6, t7,
20 | s0, s1, s2, s3, s4, s5, s6, s7,
21 | t8, t9, k0, k1, gp, sp, fp, ra,
22 |
23 | rapc,
24 | hi, lo,
25 | sr,
26 | caus,
27 | // GPR
28 |
29 | // unknown0 used for BD flag
30 | unknown0, unknown1,
31 | unknown2, unknown3,
32 | unknown4, unknown5,
33 | unknown6, unknown7,
34 | unknown9,
35 |
36 | COUNT // C# only, not present on the PSX struct
37 | }
38 |
39 | public enum PrimaryOpcode : byte {
40 | SPECIAL = 0x00,
41 | BCZ = 0x01,
42 | J = 0x02,
43 | JAL = 0x03,
44 | BEQ = 0x04,
45 | BNE = 0x05,
46 | BLEZ = 0x06,
47 | BGTZ = 0x07,
48 | ADDI = 0x08,
49 | ADDIU = 0x09,
50 | SLTI = 0X0A,
51 | SLTIU = 0X0B,
52 | ANDI = 0X0C,
53 | ORI = 0X0D,
54 | XORI = 0X0E,
55 | LUI = 0X0F,
56 | COP0 = 0X10,
57 | COP1 = 0X11,
58 | COP2 = 0X12,
59 | COP3 = 0X13,
60 | LB = 0X20,
61 | LH = 0X21,
62 | LWL = 0X22,
63 | LW = 0X23,
64 | LBU = 0X24,
65 | LHU = 0X25,
66 | LWR = 0X26,
67 | SB = 0X28,
68 | SH = 0X29,
69 | SWL = 0X2A,
70 | SW = 0X2B,
71 | SWR = 0X2E,
72 | LWC0 = 0X30,
73 | LWC1 = 0X31,
74 | LWC2 = 0X32,
75 | LWC3 = 0X33,
76 | SWC0 = 0X38,
77 | SWC1 = 0X39,
78 | SWC2 = 0X3A,
79 | SWC3 = 0X3B
80 | }
81 |
82 | public enum SecondaryOpcode : byte {
83 | SLL = 0X00,
84 | SRL = 0X02,
85 | SRA = 0X03,
86 | SLLV = 0X04,
87 | SRLV = 0X06,
88 | SRAV = 0X07,
89 | JR = 0X08,
90 | JALR = 0X09,
91 | SYSCALL = 0X0C,
92 | BREAK = 0X0D,
93 | MFHI = 0X10,
94 | MTHI = 0X11,
95 | MFLO = 0X12,
96 | MTLO = 0X13,
97 | MULT = 0X18,
98 | MULTU = 0X19,
99 | DIV = 0X1A,
100 | DIVU = 0X1B,
101 | ADD = 0X20,
102 | ADDU = 0X21,
103 | SUB = 0X22,
104 | SUBU = 0X23,
105 | AND = 0X24,
106 | OR = 0X25,
107 | XOR = 0X26,
108 | NOR = 0X27,
109 | SLT = 0X2A,
110 | SLTU = 0X2B
111 | }
112 |
113 | public enum BCZOpcode : byte {
114 | BLTZ = 0x00,
115 | BLTZAL = 0x10,
116 | BGEZ = 0x01,
117 | BGEZAL = 0x11
118 | }
119 |
120 | private static HaltState haltState = HaltState.HALT; // Console is halted by default
121 |
122 |
123 | // The PSX's Thread Control Block (usually TCB[0])
124 | public class TCB {
125 | public UInt32[] regs = new UInt32[ (int)GPR.COUNT ];
126 | }
127 |
128 | // The PSX's active thread control block
129 | // (a copy of the psx's registers at the time of breaking)
130 | public static TCB tcb = new TCB();
131 | public const int TCB_LENGTH_BYTES = (int)GPR.COUNT * 4;
132 |
133 | private static UInt32 branch_address = 0;
134 | private static bool branch_on_next_exec;
135 | private static bool step_break_set = false;
136 | private static UInt32 step_break_addr = 0;
137 |
138 | public static bool IsStepBreakSet {
139 | get { return step_break_set; }
140 | set { step_break_set = value; }
141 | }
142 |
143 | ///
144 | /// Determine target address from a branch instruction
145 | ///
146 | ///
147 | ///
148 | ///
149 | private static UInt32 CalculateBranchAddress( UInt32 opcode, bool eval ) {
150 | UInt32 offset = (opcode & 0xFFFF) << 2;
151 |
152 | if ( eval ) {
153 |
154 | if ( (offset & (1 << 17)) != 0 ) { // if bit 17 is set, sign extend
155 | offset |= 0xFFFFC000;
156 | }
157 |
158 | return offset + tcb.regs[ (int)GPR.rapc ] + 4;
159 | } else {
160 | return tcb.regs[ (int)GPR.rapc ] += 8;
161 | }
162 | }
163 |
164 | ///
165 | ///
166 | ///
167 | ///
168 | ///
169 | public static void EmulateStep( ) {
170 | UInt32 opcode;
171 | if ( tcb.regs[ (int)GPR.unknown0 ] == 0 ) {
172 | opcode = GDBServer.GetInstructionCached( tcb.regs[ (int)GPR.rapc ] );
173 | } else {
174 | branch_on_next_exec = true;
175 | opcode = GDBServer.GetInstructionCached( tcb.regs[ (int)GPR.rapc ] + 4 );
176 | }
177 | UInt32 rs = GetOneRegisterBE( (opcode >> 21) & 0x1F );
178 | UInt32 rt = GetOneRegisterBE( (opcode >> 16) & 0x1F );
179 | UInt32 rd = (opcode >> 11) & 0x1F;
180 | UInt32 immediate = opcode & 0xFFFF;
181 |
182 | //UInt32 pc = 0;
183 | bool emulated = true;
184 |
185 | switch ( GetPrimaryOpcode( opcode ) ) {
186 | case PrimaryOpcode.SPECIAL: // Special
187 | switch ( GetSecondaryOpcode( opcode ) ) {
188 | case SecondaryOpcode.ADD:
189 | SetOneRegisterBE( rd, rs + rt );
190 | break;
191 |
192 | case SecondaryOpcode.ADDU:
193 | SetOneRegisterBE( rd, rs + rt );
194 | break;
195 |
196 |
197 | case SecondaryOpcode.JR: // JR - Bits 21-25 contain the Jump Register
198 | case SecondaryOpcode.JALR: // JALR - Bits 21-25 contain the Jump Register
199 | branch_address = rs;
200 | branch_on_next_exec = true;
201 | break;
202 |
203 | default: // derp?
204 | //pc = SkipJumpInstruction( opcode );
205 | emulated = false;
206 | break;
207 | }
208 | break;
209 |
210 | case PrimaryOpcode.BCZ: // REGIMM / BcondZ
211 | switch ( GetBCZOpcode( opcode ) ) {
212 | case BCZOpcode.BLTZ: // BLTZ
213 | case BCZOpcode.BLTZAL: // BLTZAL
214 | branch_address = CalculateBranchAddress( opcode, (Int32)rs < 0 );
215 | tcb.regs[ (int)GPR.unknown0 ] = 1;
216 | break;
217 |
218 | case BCZOpcode.BGEZ: // BGEZ
219 | case BCZOpcode.BGEZAL: // BGEZAL
220 | branch_address = CalculateBranchAddress( opcode, (Int32)rs >= 0 );
221 | tcb.regs[ (int)GPR.unknown0 ] = 1;
222 | break;
223 |
224 | default: // derp?
225 | //pc = SkipJumpInstruction( opcode );
226 | emulated = false;
227 | break;
228 | }
229 | break;
230 |
231 | case PrimaryOpcode.J: // J
232 | case PrimaryOpcode.JAL: // JAL
233 | branch_address = CalculateJumpAddress( opcode );
234 | tcb.regs[ (int)GPR.unknown0 ] = 1;
235 | break;
236 |
237 | case PrimaryOpcode.BEQ: // BEQ
238 | branch_address = CalculateBranchAddress( opcode, rs == rt );
239 | tcb.regs[ (int)GPR.unknown0 ] = 1;
240 | break;
241 |
242 | case PrimaryOpcode.BNE: // BNE
243 | branch_address = CalculateBranchAddress( opcode, rs != rt );
244 | tcb.regs[ (int)GPR.unknown0 ] = 1;
245 | break;
246 |
247 | case PrimaryOpcode.BLEZ: // BLEZ
248 | branch_address = CalculateBranchAddress( opcode, (Int32)rs <= 0 );
249 | tcb.regs[ (int)GPR.unknown0 ] = 1;
250 | break;
251 |
252 | case PrimaryOpcode.BGTZ: // BGTZ
253 | branch_address = CalculateBranchAddress( opcode, (Int32)rs > 0 );
254 | tcb.regs[ (int)GPR.unknown0 ] = 1;
255 | break;
256 |
257 | default: // derp?
258 | emulated = false;
259 | break;
260 | }
261 |
262 | if ( emulated ) {
263 | if ( branch_on_next_exec ) {
264 | tcb.regs[ (int)GPR.rapc ] = branch_address;
265 | branch_on_next_exec = false;
266 | tcb.regs[ (int)GPR.unknown0 ] = 0;
267 | } else {
268 | if ( tcb.regs[ (int)GPR.unknown0 ] == 0 )
269 | tcb.regs[ (int)GPR.rapc ] += 4;
270 | }
271 |
272 | SetRegs();
273 | GDBServer.SetHaltStateInternal( HaltState.HALT, true );
274 | } else {
275 | Log.WriteLine( "Un-emulated opcode: " + opcode.ToString( "X8" ), LogType.Debug );
276 | if ( branch_on_next_exec ) {
277 | branch_on_next_exec = false;
278 | GDBServer.Step( "", false );
279 | tcb.regs[ (int)GPR.unknown0 ] = 0;
280 | } else {
281 | // Not emulated, set breakpoint 4 ahead and hope for the best
282 | SetHardwareBreakpoint( tcb.regs[ (int)GPR.rapc ] + 4 );
283 | step_break_set = true;
284 | SetRegs();
285 | }
286 |
287 | if ( TransferLogic.Cont( false ) ) {
288 | GDBServer.SetHaltStateInternal( HaltState.RUNNING, false );
289 | }
290 | }
291 | }
292 |
293 | private static UInt32 CalculateJumpAddress( UInt32 opcode ) {
294 | return ((tcb.regs[ (int)GPR.rapc ] + 4) & 0x80000000) | ((opcode & 0x03FFFFFF) << 2);
295 | }
296 |
297 |
298 | ///
299 | /// Print out the current register values in TCB(retrieved by GetRegs)
300 | ///
301 | public static void DumpRegs( LogType logType = LogType.Debug ) {
302 |
303 | int tab = 0;
304 |
305 | for ( int i = 0; i < (int)GPR.COUNT - 8; i++ ) {
306 | Log.Write( "\t " + ((GPR)i).ToString().PadLeft( 4 ) + " = 0x" + tcb.regs[ i ].ToString( "X8" ), logType );
307 | // this format won't change, so there's no issue hardcoding them
308 | if ( tab++ % 4 == 3 || i == 1 || i == 33 || i == 34 ) {
309 | Log.WriteLine("", logType);
310 | tab = 0;
311 | }
312 | }
313 | Log.WriteLine("", logType );
314 |
315 | Log.WriteLine( "BD = 0x" + tcb.regs[ (int)GPR.unknown0 ].ToString( "X" ), logType );
316 |
317 | UInt32 cause = (tcb.regs[ (int)GPR.caus ] >> 2) & 0xFF;
318 |
319 | switch ( cause ) {
320 | case 0x04:
321 | Log.WriteLine( "AdEL - Data Load or instr fetch (0x"+ cause +")\n", logType );
322 | break;
323 | case 0x05:
324 | Log.WriteLine( "AdES - Data Store (unaligned?) (0x" + cause + ")\n", logType );
325 | break;
326 | case 0x06:
327 | Log.WriteLine( "IBE - Bus Error on instr fetch (0x" + cause + ")\n", logType );
328 | break;
329 | case 0x07:
330 | Log.WriteLine( "DBE - Bus Error on data load/store (0x" + cause + ")\n", logType );
331 | break;
332 | case 0x08:
333 | Log.WriteLine( "SYS - Unconditional Syscall (0x" + cause + ")\n", logType );
334 | break;
335 | case 0x09:
336 | Log.WriteLine( "BP - Break! (0x" + cause + ")\n", logType );
337 | break;
338 | case 0x0A:
339 | Log.WriteLine( "RI - Reserved Instruction (0x" + cause + ")\n", logType );
340 | break;
341 | case 0x0B:
342 | Log.WriteLine( "CpU - Coprocessor unavailable (0x" + cause + ")\n", logType );
343 | break;
344 | case 0x0C:
345 | Log.WriteLine( "Ov - Arithmetic overflow (0x" + cause + ")\n", logType ); ;
346 | break;
347 |
348 | default:
349 | Log.WriteLine( "Code " + cause +"!\n", logType );
350 | break;
351 | }
352 | }
353 |
354 | private static BCZOpcode GetBCZOpcode( UInt32 opcode ) {
355 | return (BCZOpcode)((opcode >> 16) & 0x1F);
356 | }
357 |
358 |
359 | ///
360 | /// Get the console's state
361 | ///
362 | public static HaltState GetHaltState() {
363 | return haltState;
364 | }
365 |
366 | public static void SetHaltState( HaltState state ) {
367 | haltState = state;
368 | }
369 |
370 | public static void SetHardwareBreakpoint( UInt32 address ) {
371 | lock ( SerialTarget.serialLock ) {
372 | TransferLogic.Command_SetBreakOnExec( address );
373 | }
374 | }
375 |
376 | ///
377 | /// Return a single register from TCB(retrieved by GetRegs)
378 | /// Returns the value in big endian format
379 | ///
380 | ///
381 | ///
382 | public static uint GetOneRegisterBE( uint reg ) {
383 | uint result;
384 | uint value = GetOneRegisterLE( reg );
385 |
386 | result = ((value >> 24) & 0xff) | ((value >> 8) & 0xff00) | ((value << 8) & 0xff0000) | ((value << 24) & 0xff000000);
387 |
388 | return result;
389 | }
390 |
391 | ///
392 | /// Return a single register from TCB(retrieved by GetRegs)
393 | /// Returns the value in little endian format(the default)
394 | ///
395 | ///
396 | ///
397 | public static uint GetOneRegisterLE( uint reg ) {
398 | uint value = 0;
399 | if ( reg == 0 ) value = 0;
400 | else if ( reg < 32 ) value = tcb.regs[ reg + 2 ];
401 | else if ( reg == 32 ) value = tcb.regs[ (int)GPR.stat ];
402 | else if ( reg == 33 ) value = tcb.regs[ (int)GPR.lo ];
403 | else if ( reg == 34 ) value = tcb.regs[ (int)GPR.hi ];
404 | else if ( reg == 35 ) value = tcb.regs[ (int)GPR.badv ];
405 | else if ( reg == 36 ) value = tcb.regs[ (int)GPR.caus ];
406 | else if ( reg == 37 ) value = tcb.regs[ (int)GPR.rapc ];
407 |
408 | return value;
409 | }
410 |
411 | private static PrimaryOpcode GetPrimaryOpcode( UInt32 opcode ) {
412 | return (PrimaryOpcode)(opcode >> 26);
413 | }
414 |
415 | ///
416 | /// Retrieve the regs from the PSX
417 | ///
418 | ///
419 | public static bool GetRegs() {
420 | bool got_regs = false;
421 | byte[] ptrBuffer = new byte[ 4 ];
422 |
423 | lock ( SerialTarget.serialLock ) {
424 |
425 | bool wasRunning = haltState == HaltState.RUNNING;
426 |
427 | if ( wasRunning )
428 | TransferLogic.Halt( false );
429 |
430 |
431 | // read the pointer to TCB[0]
432 | if ( TransferLogic.ReadBytes( 0x80000110, 4, ptrBuffer ) ) {
433 | UInt32 tcbPtr = BitConverter.ToUInt32( ptrBuffer, 0 );
434 | //Console.WriteLine( "TCB PTR " + tcbPtr.ToString( "X" ) );
435 |
436 | byte[] tcbBytes = new byte[ TCB_LENGTH_BYTES ];
437 | if ( TransferLogic.ReadBytes( tcbPtr, (int)GPR.COUNT * 4, tcbBytes ) ) {
438 | Buffer.BlockCopy( tcbBytes, 0, tcb.regs, 0, tcbBytes.Length );
439 |
440 | got_regs = true;
441 | }
442 |
443 | /*if ( tcb.regs[ (int)GPR.unknown0 ] == 1 ) {
444 | // Move PC to next instruction if we're in a branch delay slot
445 | tcb.regs[ (int)GPR.rapc ] += 4;
446 | }*/
447 | }
448 |
449 | if ( wasRunning )
450 | TransferLogic.Cont( false );
451 | }
452 |
453 | return got_regs;
454 | }
455 |
456 | private static SecondaryOpcode GetSecondaryOpcode( UInt32 opcode ) {
457 | return (SecondaryOpcode)(opcode & 0x3F);
458 | }
459 |
460 | public static void HardwareStep() {
461 | UInt32 address;
462 |
463 | // To-do: Emulate opcodes except for load and store?
464 | if ( tcb.regs[ (int)GPR.unknown0 ] == 0 ) {
465 | // Not in BD, step one instruction
466 | address = tcb.regs[ (int)GPR.rapc ] += 4;
467 | } else {
468 | // We're in a branch delay slot, So we need to emulate
469 | // the previous opcode to find the next instruction.
470 | // To-do: Re-purpose this to emulate other instructions
471 | address = JumpAddressFromOpcode( GDBServer.GetInstructionCached( tcb.regs[ (int)GPR.rapc ] ) );
472 |
473 | }
474 |
475 | lock ( SerialTarget.serialLock ) {
476 | TransferLogic.Command_SetBreakOnExec( address );
477 | }
478 |
479 | step_break_set = true;
480 | step_break_addr = address;
481 | }
482 |
483 | public static bool IsBranchInstruction( UInt32 opcode ) {
484 | PrimaryOpcode primary_opcode;
485 | SecondaryOpcode secondary_opcode;
486 |
487 | primary_opcode = GetPrimaryOpcode( opcode );
488 | secondary_opcode = GetSecondaryOpcode( opcode );
489 |
490 | if ( primary_opcode == PrimaryOpcode.SPECIAL && secondary_opcode == SecondaryOpcode.BREAK )
491 | return true;
492 |
493 | return false;
494 | }
495 |
496 | public static bool IsBreakInstruction( UInt32 opcode ) {
497 | PrimaryOpcode primary_opcode;
498 | SecondaryOpcode secondary_opcode;
499 |
500 | primary_opcode = GetPrimaryOpcode( opcode );
501 | secondary_opcode = GetSecondaryOpcode( opcode );
502 |
503 | if ( primary_opcode == PrimaryOpcode.SPECIAL && secondary_opcode == SecondaryOpcode.BREAK )
504 | return true;
505 |
506 | return false;
507 | }
508 |
509 |
510 | ///
511 | /// Evaluate an instruciton to determine the address of the next instruction
512 | /// Used to decide where to place the next breakpoint when stepping.
513 | ///
514 | ///
515 | ///
516 | public static UInt32 JumpAddressFromOpcode( UInt32 opcode ) {
517 | UInt32 rs = GetOneRegisterLE( (opcode >> 21) & 0x1F );
518 | UInt32 rt = GetOneRegisterLE( (opcode >> 16) & 0x1F );
519 |
520 | UInt32 address;
521 |
522 | switch ( GetPrimaryOpcode( opcode ) ) {
523 | case PrimaryOpcode.SPECIAL: // Special
524 | switch ( GetSecondaryOpcode( opcode ) ) {
525 | case SecondaryOpcode.JR: // JR - Bits 21-25 contain the Jump Register
526 | case SecondaryOpcode.JALR: // JALR - Bits 21-25 contain the Jump Register
527 | address = rs;
528 | break;
529 |
530 | default: // derp?
531 | address = SkipJumpInstruction( opcode );
532 | break;
533 | }
534 | break;
535 |
536 | case PrimaryOpcode.BCZ: // REGIMM / BcondZ
537 | switch ( GetBCZOpcode( opcode ) ) {
538 | case BCZOpcode.BLTZ: // BLTZ
539 | case BCZOpcode.BLTZAL: // BLTZAL
540 | address = CalculateBranchAddress( opcode, (Int32)rs < 0 );
541 | break;
542 |
543 | case BCZOpcode.BGEZ: // BGEZ
544 | case BCZOpcode.BGEZAL: // BGEZAL
545 | address = CalculateBranchAddress( opcode, (Int32)rs >= 0 );
546 | break;
547 |
548 | default: // derp?
549 | address = SkipJumpInstruction( opcode );
550 | break;
551 | }
552 | break;
553 |
554 | case PrimaryOpcode.J: // J
555 | case PrimaryOpcode.JAL: // JAL
556 | address = CalculateJumpAddress( opcode );
557 | break;
558 |
559 | case PrimaryOpcode.BEQ: // BEQ
560 | address = CalculateBranchAddress( opcode, rs == rt );
561 | break;
562 |
563 | case PrimaryOpcode.BNE: // BNE
564 | address = CalculateBranchAddress( opcode, rs != rt );
565 | break;
566 |
567 | case PrimaryOpcode.BLEZ: // BLEZ
568 | address = CalculateBranchAddress( opcode, (Int32)rs <= 0 );
569 | break;
570 |
571 | case PrimaryOpcode.BGTZ: // BGTZ
572 | address = CalculateBranchAddress( opcode, (Int32)rs > 0 );
573 | break;
574 |
575 | default: // derp?
576 | address = SkipJumpInstruction( opcode );
577 | break;
578 | }
579 |
580 | return address;
581 | }
582 |
583 | ///
584 | /// Set a single register in TCB(set by SetRegs)
585 | /// Value given in little endian format( the default)
586 | ///
587 | ///
588 | ///
589 | private static void SetOneRegisterLE( uint reg, uint value ) {
590 | if ( reg < 32 ) tcb.regs[ reg + 2 ] = value;
591 | if ( reg == 32 ) tcb.regs[ (int)GPR.stat ] = value;
592 | if ( reg == 33 ) tcb.regs[ (int)GPR.lo ] = value;
593 | if ( reg == 34 ) tcb.regs[ (int)GPR.hi ] = value;
594 | if ( reg == 35 ) tcb.regs[ (int)GPR.badv ] = value;
595 | if ( reg == 36 ) tcb.regs[ (int)GPR.caus ] = value;
596 | if ( reg == 37 ) tcb.regs[ (int)GPR.rapc ] = value;
597 | }
598 |
599 | ///
600 | /// Set a single register in TCB(set by SetRegs)
601 | /// Value given in big endian format
602 | ///
603 | ///
604 | ///
605 | public static void SetOneRegisterBE( uint reg, uint value ) {
606 | value = ((value & 0xff000000) >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) |
607 | ((value & 0x000000ff) << 24);
608 |
609 | SetOneRegisterLE( reg, value );
610 | }
611 |
612 | ///
613 | /// Write registers back to the PSX
614 | ///
615 | ///
616 | public static bool SetRegs() {
617 |
618 | // read the pointer to TCB[0]
619 | byte[] ptrBuffer = new byte[ 4 ];
620 | if ( !TransferLogic.ReadBytes( 0x80000110, 4, ptrBuffer ) ) {
621 | return false;
622 | }
623 |
624 | UInt32 tcbPtr = BitConverter.ToUInt32( ptrBuffer, 0 );
625 | //Console.WriteLine( "TCB PTR " + tcbPtr.ToString( "X" ) );
626 |
627 | // Convert regs back to a byte array and bang them back out
628 | byte[] tcbBytes = new byte[ TCB_LENGTH_BYTES ];
629 | Buffer.BlockCopy( tcb.regs, 0, tcbBytes, 0, TCB_LENGTH_BYTES );
630 |
631 | TransferLogic.Command_SendBin( tcbPtr, tcbBytes );
632 |
633 | return true;
634 | }
635 |
636 | ///
637 | /// Unknown opcode detected when determining next PC, march on and hope for the best.
638 | ///
639 | ///
640 | ///
641 | private static UInt32 SkipJumpInstruction( UInt32 opcode ) {
642 | Log.WriteLine( "Unknown instruction above delay slot: " + opcode.ToString( "X8" ), LogType.Debug );
643 | return tcb.regs[ (int)GPR.rapc ] += 8;
644 | }
645 |
646 | public static void StepBreakCallback() {
647 | UInt32 current_pc = (tcb.regs[ (int)GPR.unknown0 ] == 0) ? tcb.regs[ (int)GPR.rapc ] : tcb.regs[ (int)GPR.rapc ] + 4;
648 | TransferLogic.Unhook();
649 | step_break_set = false;
650 | if ( current_pc != step_break_addr ) {
651 | Log.WriteLine( "Stopped at unexpected step address " + current_pc.ToString( "X8" ) + " instead of " + step_break_addr.ToString( "X8" ), LogType.Debug );
652 | }
653 | }
654 | }
655 |
--------------------------------------------------------------------------------
/ELFSharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/ELFSharp.dll
--------------------------------------------------------------------------------
/GDBServer.cs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | //
6 | // GDB TCP->SIO bridge for Unirom 8
7 | //
8 | // NOTE!
9 | //
10 | // While all of the basic debug functionality is present, the GDB
11 | // bridge is still very much a work in progress!
12 | //
13 | // Many thanks to Nicolas "Pixel" Noble
14 | // This GDB server is heavily based on his GDB server implementation
15 | // in PCSX-Redux. Some bits were borrowed and wisdom called upon
16 | // when things broke.
17 | // -John "Skitchin" Baumann
18 |
19 | // TODO: set running/halted state when reconnecting
20 |
21 | // TODO: Handle software breakpoints internally?
22 | // If we break/step on a BD, the original branch instruction must be
23 | // the next PC. We could just lie to GDB about our PC but gdb will
24 | // try to shove it's software breakpoint in place.
25 |
26 | // TODO: Add 4 to the PC if we're in a branch delay slot. (see above first)
27 | // TODO: Split GDB server code from emulation logic, cache, etc?
28 | // TODO: Continue and Step both take optional address arguments, needs testing.
29 | // TODO: Begin moving non-gdb-specific code to new classes
30 | // i.e. CPUHelper for emulation, calculating branches, etc
31 |
32 | using System;
33 | using System.Text;
34 | using System.IO.Ports;
35 | using System.Net;
36 | using System.Net.Sockets;
37 | using System.Threading;
38 | using System.Globalization;
39 | using System.Collections.Generic;
40 |
41 |
42 | public class GDBServer {
43 |
44 | private static bool _enabled = false;
45 | public static bool IsEnabled => _enabled;
46 |
47 | private static bool emulate_steps = false;
48 |
49 | private static Dictionary original_opcode = new Dictionary();
50 |
51 | public static TargetDataPort serial => Program.activeSerial;
52 |
53 | private static bool ack_enabled = true;
54 | private static bool manifest_enabled = true;
55 |
56 | const string memoryMap = @"
57 |
58 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | ";
94 |
95 | const string targetXML = @"
96 |
97 |
98 |
99 |
100 | mips:3000
101 | none
102 |
103 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | ";
190 |
191 | // For joining parts of the TCP stream
192 | // TODO: there's no checks to stop this getting out of hand
193 | private static bool stitchingPacketsTogether = false;
194 | private static string activePacketString = "";
195 |
196 |
197 |
198 |
199 | ///
200 | /// Calculate the checksum for the packet
201 | ///
202 | ///
203 | ///
204 | private static string CalculateChecksum( string packet ) {
205 |
206 | byte checksum = 0;
207 | foreach ( char c in packet ) {
208 | checksum += (byte)c;
209 | }
210 |
211 | //checksum %= (byte)256;
212 | return checksum.ToString( "X2" );
213 | }
214 |
215 |
216 | private static void Continue( string data ) {
217 | // TODO: specify an addr?
218 | Log.WriteLine( "Got continue request", LogType.Debug );
219 | if ( data.Length == 9 ) {
220 | // UNTESTED
221 | // To-do: Test it.
222 | // Got memory address to continue to
223 | Log.WriteLine( "Got memory address to continue to", LogType.Debug );
224 | CPU.SetHardwareBreakpoint( UInt32.Parse( data.Substring( 1, 8 ), NumberStyles.HexNumber ) );
225 | }
226 | lock ( SerialTarget.serialLock ) {
227 | if ( TransferLogic.Cont( false ) ) {
228 | SetHaltStateInternal( CPU.HaltState.RUNNING, false );
229 | }
230 | }
231 |
232 | SendGDBResponse( "OK" );
233 | }
234 |
235 | ///
236 | /// Respond to GDB Detach 'D' packet
237 | ///
238 | private static void Detach() {
239 | Log.WriteLine( "Detaching from target...", LogType.Info );
240 |
241 | // Do some stuff to detach from the target
242 | // Close & restart server
243 | ResetConnection();
244 |
245 | SendGDBResponse( "OK" );
246 | }
247 |
248 | public static void DisableManifest() {
249 | manifest_enabled = false;
250 | }
251 |
252 | ///
253 | /// Respond to GDB Extended Mode '!' packet
254 | ///
255 | private static void EnableExtendedMode() {
256 | SendGDBResponse( "OK" );
257 | }
258 |
259 | /*public static void EnableManifest() {
260 | manifest_enabled = true;
261 | }*/
262 |
263 | ///
264 | /// Attempt to locate an instruction from cache,
265 | /// otherwise grab it from ram.
266 | ///
267 | ///
268 | ///
269 | public static UInt32 GetInstructionCached( UInt32 address ) {
270 | byte[] read_buffer = new byte[ 4 ];
271 | UInt32 opcode = 0;
272 |
273 | if ( original_opcode.ContainsKey( address ) ) {
274 | opcode = original_opcode[ address ];
275 | } else {
276 | if ( GetMemory( address, 4, read_buffer ) ) {
277 | // To-do: Maybe grab larger chunks and parse
278 | opcode = BitConverter.ToUInt32( read_buffer, 0 );
279 | original_opcode[ address ] = opcode;
280 | }
281 | }
282 |
283 | return opcode;
284 | }
285 |
286 | ///
287 | /// Grab data from Unirom
288 | ///
289 | ///
290 | ///
291 | ///
292 | ///
293 | public static bool GetMemory( uint address, uint length, byte[] data ) {
294 | Log.WriteLine( "Getting memory from 0x" + address.ToString( "X8" ) + " for " + length.ToString() + " bytes", LogType.Debug );
295 |
296 | lock ( SerialTarget.serialLock ) {
297 | if ( !TransferLogic.ReadBytes( address, length, data ) ) {
298 | Log.WriteLine( "Couldn't read bytes from Unirom!", LogType.Error );
299 | return false;
300 | }
301 | }
302 |
303 | return true;
304 | }
305 |
306 |
307 | ///
308 | /// Convert a hex nibble char to a byte
309 | ///
310 | ///
311 | ///
312 | private static byte GimmeNibble( char inChar ) {
313 |
314 | // TODO: lol not this
315 | /*switch ( inChar ) {
316 | case '0': return 0x0;
317 | case '1': return 0x1;
318 | case '2': return 0x2;
319 | case '3': return 0x3;
320 | case '4': return 0x4;
321 | case '5': return 0x5;
322 | case '6': return 0x6;
323 | case '7': return 0x7;
324 | case '8': return 0x8;
325 | case '9': return 0x9;
326 | case 'A': case 'a': return 0xA;
327 | case 'B': case 'b': return 0xB;
328 | case 'C': case 'c': return 0xC;
329 | case 'D': case 'd': return 0xD;
330 | case 'E': case 'e': return 0xE;
331 | case 'F': case 'f': return 0xF;
332 | }
333 | return 0;*/
334 |
335 | // Maybe this instead?
336 | if ( inChar >= '0' && inChar <= '9' )
337 | return (byte)(inChar - 48);
338 |
339 | else if ( inChar >= 'A' && inChar <= 'F' )
340 | return (byte)(inChar - 55);
341 |
342 | else if ( inChar >= 'a' && inChar <= 'f' )
343 | return (byte)(inChar - 87);
344 |
345 | // Not a valid hex char
346 | else return 0;
347 | }
348 |
349 | ///
350 | /// User pressed Ctrl+C, do a thing
351 | ///
352 | private static void HandleCtrlC() {
353 |
354 | lock ( SerialTarget.serialLock ) {
355 | if ( TransferLogic.Halt( false ) )
356 | SetHaltStateInternal( CPU.HaltState.HALT, true );
357 | }
358 |
359 | }
360 |
361 | ///
362 | /// Double check that the console's there
363 | /// when starting up
364 | ///
365 | public static void Init() {
366 |
367 | Log.WriteLine( "Checking if Unirom is in debug mode...", LogType.Debug );
368 |
369 | // if it returns true, we might enter /m (monitor) mode, etc
370 | if (
371 | !TransferLogic.ChallengeResponse( CommandMode.DEBUG )
372 | ) {
373 | Log.WriteLine( "Couldn't determine if Unirom is in debug mode.", LogType.Error );
374 | return;
375 | }
376 |
377 | // More of a test than a requirement...
378 | Log.WriteLine( "Grabbing initial state...", LogType.Debug );
379 | CPU.DumpRegs();
380 |
381 | Log.WriteLine( "GDB server initialised" );
382 | _enabled = true;
383 | }
384 |
385 |
386 |
387 |
388 | ///
389 | /// Respond to GDB Memory Read 'm' packet
390 | ///
391 | ///
392 | private static void MemoryRead( string data ) {
393 | string[] parts = data.Substring( 1 ).Split( ',' );
394 | uint address = uint.Parse( parts[ 0 ], System.Globalization.NumberStyles.HexNumber );
395 | uint length = uint.Parse( parts[ 1 ], System.Globalization.NumberStyles.HexNumber );
396 | byte[] read_buffer = new byte[ length ];
397 | string response = "";
398 |
399 | //ReadCached( address, length, read_buffer );
400 | GetMemory( address, length, read_buffer );
401 |
402 | for ( uint i = 0; i < length; i++ ) {
403 | response += read_buffer[ i ].ToString( "X2" );
404 | }
405 |
406 | //Console.WriteLine( "MemoryRead @ 0x"+ address.ToString("X8") + ":" + response );
407 | SendGDBResponse( response );
408 | }
409 |
410 |
411 | ///
412 | /// Parse and upload an $M packet - e.g. as a result of `load` in GDB
413 | ///
414 | ///
415 | private static void MemoryWrite( string data ) {
416 |
417 | // TODO: validate memory regions
418 |
419 | UInt32 address = UInt32.Parse( data.Substring( 1, data.IndexOf(",") - 1 ), NumberStyles.HexNumber );
420 |
421 | // Where in the string do we find the addr substring
422 | int sizeStart = data.IndexOf( "," ) + 1;
423 | int sizeEnd = data.IndexOf( ":" );
424 | UInt32 length = UInt32.Parse( data.Substring( sizeStart, (sizeEnd - sizeStart) ), NumberStyles.HexNumber );
425 | byte[] bytes_out = ParseHexBytes( data, sizeEnd + 1, length );
426 |
427 | if ( !original_opcode.ContainsKey( address ) ) {
428 |
429 | PareseToCache( address, length, bytes_out );
430 | }
431 |
432 |
433 | lock ( SerialTarget.serialLock ) {
434 | TransferLogic.Command_SendBin( address, bytes_out );
435 | }
436 |
437 | SendGDBResponse( "OK" );
438 | }
439 |
440 | private static void PareseToCache( UInt32 address, UInt32 length, byte[] read_buffer ) {
441 | UInt32 instruction;
442 |
443 | for ( uint i = 0; i < length; i += 4 ) {
444 | if ( length - i < 4 )
445 | break; // derp?
446 |
447 | instruction = BitConverter.ToUInt32( read_buffer, (int)i );
448 |
449 | if ( !original_opcode.ContainsKey( address + i ) && !CPU.IsBreakInstruction( instruction ) ) {
450 | original_opcode[ address + i ] = instruction;
451 | }
452 | }
453 | }
454 |
455 | ///
456 | /// Parse a string of hex bytes (no preceding 0x)
457 | ///
458 | ///
459 | ///
460 | ///
461 | ///
462 | ///
463 | public static byte[] ParseHexBytes( string inString, int startChar, UInt32 numBytesToRead ) {
464 |
465 | if ( inString.Length < startChar + (numBytesToRead * 2) ) {
466 | throw new IndexOutOfRangeException( "Input string is too short!" );
467 | }
468 |
469 | byte[] outBytes = new byte[ numBytesToRead ];
470 |
471 | byte activeByte;
472 | int charPos = 0;
473 |
474 | for ( int i = startChar; i < startChar + (numBytesToRead * 2); i += 2 ) {
475 | char first = inString[ i ];
476 | char second = inString[ i + 1 ];
477 | activeByte = (byte)((GimmeNibble( first ) << 4) | GimmeNibble( second ));
478 | outBytes[ charPos++ ] = activeByte;
479 | }
480 |
481 | return outBytes;
482 | }
483 |
484 |
485 | ///
486 | /// Receive a command and do some stuff with it
487 | ///
488 | ///
489 | private static void ProcessCommand( string data ) {
490 |
491 | //Console.WriteLine( "Got command " + data );
492 |
493 | switch ( data[ 0 ] ) {
494 | case '!':
495 | EnableExtendedMode();
496 | break;
497 |
498 | case '?':
499 | QueryHaltReason();
500 | break;
501 |
502 | case 'c': // Continue - c [addr]
503 | Continue( data );
504 | break;
505 |
506 | case 's': // Step - s [addr]
507 | lock ( SerialTarget.serialLock ) {
508 | Step( data, emulate_steps );
509 | }
510 | break;
511 |
512 | case 'D': // Detach
513 | Detach();
514 | break;
515 |
516 | case 'g': // Get registers
517 | ReadRegisters();
518 | break;
519 |
520 | case 'G': // Write registers
521 | WriteRegisters( data );
522 | break;
523 |
524 | case 'H': // thread stuff
525 | if ( data.StartsWith( "Hc0" ) ) {
526 | SendGDBResponse( "OK" );
527 | } else if ( data.StartsWith( "Hc-1" ) ) {
528 | SendGDBResponse( "OK" );
529 | } else if ( data.StartsWith( "Hg0" ) ) {
530 | SendGDBResponse( "OK" );
531 | } else Unimplemented( data );
532 | break;
533 |
534 | case 'm': // Read memory
535 | MemoryRead( data );
536 | break;
537 |
538 | case 'M': // Write memory
539 | MemoryWrite( data );
540 | break;
541 |
542 | case 'p': // Read single register
543 | ReadRegister( data );
544 | break;
545 |
546 | case 'P': // Write single register
547 | WriteRegister( data );
548 | break;
549 |
550 | case 'q':
551 | if ( data.StartsWith( "qAttached" ) ) {
552 | SendGDBResponse( "1" );
553 | } else if ( data.StartsWith( "qC" ) ) {
554 | // Get Thread ID, always 00
555 | SendGDBResponse( "QC00" );
556 | } else if ( data.StartsWith( "qSupported" ) ) {
557 | if ( manifest_enabled ) {
558 | SendGDBResponse( "PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:memory-map:read+;QStartNoAckMode+" );
559 | } else {
560 | SendGDBResponse( "PacketSize=4000;qXfer:threads:read+;QStartNoAckMode+" );
561 | }
562 |
563 | } else if ( manifest_enabled && data.StartsWith( "qXfer:features:read:target.xml:" ) ) {
564 | SendPagedResponse( targetXML );
565 | } else if ( data.StartsWith( "qXfer:memory-map:read::" ) ) {
566 | SendPagedResponse( memoryMap );
567 | } else if ( data.StartsWith( "qXfer:threads:read::" ) ) {
568 | SendPagedResponse( "" );
569 | } else if ( data.StartsWith( "qRcmd" ) ) {
570 | // To-do: Process monitor commands
571 | ProcessMonitorCommand( data );
572 | } else Unimplemented( data );
573 | break;
574 |
575 | case 'Q':
576 | if ( data.StartsWith( "QStartNoAckMode" ) ) {
577 | SendGDBResponse( "OK" );
578 | ack_enabled = false;
579 | } else Unimplemented( data );
580 | break;
581 |
582 | case 'v':
583 | if ( data.StartsWith( "vAttach" ) ) {
584 | //
585 | Unimplemented( data );
586 | } else if ( data.StartsWith( "vMustReplyEmpty" ) ) {
587 | SendGDBResponse( "" );
588 | } else if ( data.StartsWith( "vKill;" ) ) {
589 | // Kill the process
590 | SendGDBResponse( "OK" );
591 | } else Unimplemented( data );
592 | break;
593 |
594 | case 'X':
595 | // Write data to memory
596 |
597 | // E.g. to signal the start of mem writes with
598 | // $Xffffffff8000f800,0:#e4
599 | //Console.WriteLine( "Pausing the PSX for uploads..." );
600 | lock ( SerialTarget.serialLock ) {
601 | TransferLogic.ChallengeResponse( CommandMode.HALT );
602 | }
603 | SendGDBResponse( "" );
604 | break;
605 |
606 | // Comment out, let GDB manage writing breakpoints
607 | // To-do: Consider tracking/setting breakpoints in our GDB stub
608 | /*case 'Z':
609 | // Set breakpoint
610 | SetBreakpoint( data );
611 |
612 | break;
613 |
614 | case 'z':
615 | SendGDBResponse( "" );
616 | break;*/
617 |
618 | default:
619 | Unimplemented( data );
620 | break;
621 | }
622 |
623 | }
624 |
625 |
626 | ///
627 | /// Get data and do a thing with it
628 | ///
629 | ///
630 | public static void ProcessData( string Data ) {
631 |
632 | char[] packet = Data.ToCharArray();
633 | string packetData = "";
634 | string our_checksum = "0";
635 | int offset = 0;
636 | int size = Data.Length;
637 |
638 | // This one isn't sent in plain text
639 | if ( Data[ 0 ] == (byte)0x03 ) {
640 | //Console.WriteLine( "Got a ^C" );
641 | HandleCtrlC();
642 | return;
643 | }
644 |
645 | // TODO: this could maybe be done nicer?
646 | if ( stitchingPacketsTogether ) {
647 | // rip GC, #yolo
648 | //Console.WriteLine( "Adding partial packet, len= " + Data.Length );
649 | activePacketString += Data;
650 | // did we reach the end?
651 | if ( Data.IndexOf( "#" ) == Data.Length - 2 - 1 ) {
652 | stitchingPacketsTogether = false;
653 | // now re-call this function with the completed packet
654 | ProcessData( activePacketString );
655 | }
656 | return;
657 | }
658 |
659 | //Console.WriteLine( "Processing data: " + Data );
660 | while ( size > 0 ) {
661 | char c = packet[ offset++ ];
662 | size--;
663 | if ( c == '+' ) {
664 | SendAck();
665 | }
666 | if ( c == '$' ) {
667 | int end = Data.IndexOf( '#', offset );
668 | if ( end == -1 ) {
669 | //Console.WriteLine( "Partial packet, len=" + Data.Length );
670 | stitchingPacketsTogether = true;
671 | activePacketString = Data;
672 | return;
673 | }
674 |
675 | packetData = Data.Substring( offset, end - offset );
676 | //Console.WriteLine( "Packet data: " + packetData );
677 | our_checksum = CalculateChecksum( packetData );
678 | size -= (end - offset);
679 | offset = end;
680 | } else if ( c == '#' ) {
681 | string checksum = Data.Substring( offset, 2 );
682 | //Console.WriteLine( "Checksum: " + checksum );
683 | //Console.WriteLine( "Our checksum: " + our_checksum );
684 | if ( checksum.ToUpper().Equals( our_checksum ) ) {
685 | //Console.WriteLine( "Checksums match!" );
686 | if ( ack_enabled )
687 | SendAck();
688 |
689 | ProcessCommand( packetData );
690 | //Bridge.Send( "$" + packetData + "#" + CalculateChecksum( packetData ));
691 | //ProcessPacket( packetData );
692 | } else {
693 | Log.WriteLine( "Checksums don't match!", LogType.Error );
694 | }
695 | offset += 2;
696 | size -= 3;
697 | } else if ( c == '-' ) {
698 | Log.WriteLine( "Negative ACK", LogType.Error );
699 | }
700 | }
701 | }
702 |
703 | private static void ProcessMonitorCommand( string data ) {
704 | Log.WriteLine( "Got qRcmd: " + data, LogType.Debug );
705 | SendGDBResponse( "OK" );
706 | }
707 |
708 |
709 | ///
710 | /// Respond to GDB Query '?' packet
711 | ///
712 | private static void QueryHaltReason() {
713 | switch ( CPU.GetHaltState() ) {
714 | case CPU.HaltState.RUNNING: SendGDBResponse( "S00" ); break;
715 | case CPU.HaltState.HALT: SendGDBResponse( "S05" ); break;
716 | }
717 | }
718 |
719 | private static void ReadCached( UInt32 address, UInt32 length, byte[] read_buffer ) {
720 | UInt32 instruction;
721 |
722 |
723 | // Check for data 4 bytes at a time
724 | // If not found, fetch memory and push it to cache + buffer
725 |
726 | // Just grab the whole chunk for now if we don't have the start
727 | if ( original_opcode.ContainsKey( address ) ) {
728 | for ( uint i = 0; i < length; i += 4 ) {
729 | instruction = GetInstructionCached( address + i );
730 | Array.Copy( BitConverter.GetBytes( instruction ), 0, read_buffer, i, (length - i < 4) ? length - i : 4 );
731 | }
732 | } else {
733 | GetMemory( address, length, read_buffer );
734 | PareseToCache( address, length, read_buffer );
735 | }
736 | }
737 |
738 | ///
739 | /// Respond to GDB Read Register 'p' packet
740 | ///
741 | ///
742 | private static void ReadRegister( string data ) {
743 | if ( (data.Length != 12) || (data.Substring( 3, 1 ) != "=") ) {
744 | SendGDBResponse( "E00" );
745 | } else {
746 | uint reg_num = uint.Parse( data.Substring( 1, 2 ), System.Globalization.NumberStyles.HexNumber );
747 |
748 | lock ( SerialTarget.serialLock ) {
749 |
750 | bool wasRunning = CPU.GetHaltState() == CPU.HaltState.RUNNING;
751 |
752 | if ( wasRunning )
753 | TransferLogic.Halt( false );
754 |
755 | CPU.GetRegs();
756 |
757 | if ( wasRunning )
758 | TransferLogic.Cont( false );
759 |
760 | }
761 | CPU.GetOneRegisterBE( reg_num ).ToString( "X8" );
762 |
763 | }
764 | }
765 |
766 | ///
767 | /// Respond to GDB Read Register 'g' packet
768 | ///
769 | private static void ReadRegisters() {
770 | string register_data = "";
771 |
772 | CPU.GetRegs();
773 |
774 | for ( uint i = 0; i < 72; i++ )
775 | register_data += CPU.GetOneRegisterBE( i ).ToString( "X8" );
776 |
777 | SendGDBResponse( register_data );
778 | }
779 |
780 | public static void ResetConnection() {
781 | ack_enabled = true;
782 | }
783 |
784 | ///
785 | /// Send GDB a packet acknowledgement(only in ack mode)
786 | ///
787 | private static void SendAck() {
788 | Bridge.Send( "+" );
789 | }
790 |
791 | ///
792 | /// The main function used for replying to GDB
793 | ///
794 | ///
795 | private static void SendGDBResponse( string response ) {
796 | Bridge.Send( "$" + response + "#" + CalculateChecksum( response ) );
797 | }
798 |
799 | ///
800 | /// Send GDB a Paged response
801 | ///
802 | ///
803 | private static void SendPagedResponse( string response ) {
804 | Bridge.Send( "$l" + response + "#" + CalculateChecksum( response ) );
805 | }
806 |
807 | ///
808 | /// Set the console's state
809 | ///
810 | ///
811 | ///
812 | public static void SetHaltStateInternal( CPU.HaltState inState, bool notifyGDB ) {
813 | CPU.SetHaltState( inState );
814 | if ( notifyGDB ) {
815 | if ( CPU.GetHaltState() == CPU.HaltState.RUNNING ) {
816 | SendGDBResponse( "S00" );
817 | } else {
818 | SendGDBResponse( "S05" );
819 | }
820 | }
821 | }
822 |
823 | ///
824 | /// Respond to a GDB Step 's' packet
825 | ///
826 | ///
827 | ///
828 | public static void Step( string data, bool use_emulation ) {
829 |
830 | if ( data.Length > 1 ) {
831 | Log.WriteLine( "Hrm?", LogType.Debug );
832 | }
833 |
834 |
835 | // This isn't really fleshed out, disabled for now.
836 | // Attempt to emulate instructions internally rather than firing them on console
837 | // If there is something we can't handle, recurse with use_emulation = false
838 | if ( use_emulation ) {
839 | CPU.EmulateStep();
840 | // Notify GDB of "halt"?
841 | } else {
842 | /*if ( data.Length == 9 ) {
843 | // UNTESTED
844 | // To-do: Test it.
845 | // Got memory address to step to
846 | Log.ToScreen( "Got memory address to step to", LogType.Debug );
847 | next_pc = UInt32.Parse( data.Substring( 1, 8 ), NumberStyles.HexNumber );
848 | } else {
849 |
850 | }*/
851 | CPU.HardwareStep();
852 |
853 | SendGDBResponse( "OK" );
854 |
855 | if ( TransferLogic.Cont( false ) ) {
856 | SetHaltStateInternal( CPU.HaltState.RUNNING, false );
857 | }
858 | }
859 | }
860 |
861 | ///
862 | ///
863 | ///
864 | ///
865 | private static void Unimplemented( string data ) {
866 | SendGDBResponse( "" );
867 | Log.WriteLine( "Got unimplemented gdb command " + data + ", reply empty", LogType.Debug );
868 | }
869 |
870 | ///
871 | /// Respond to GDB Write Register 'P' packet
872 | ///
873 | ///
874 | private static void WriteRegister( string data ) {
875 |
876 | if ( (data.Length != 12) || (data.Substring( 3, 1 ) != "=") ) {
877 | SendGDBResponse( "E00" );
878 | } else {
879 |
880 | uint reg_num = uint.Parse( data.Substring( 1, 2 ), System.Globalization.NumberStyles.HexNumber );
881 | uint reg_value = uint.Parse( data.Substring( 4, 8 ), System.Globalization.NumberStyles.HexNumber );
882 |
883 | lock ( SerialTarget.serialLock ) {
884 |
885 | bool wasRunning = CPU.GetHaltState() == CPU.HaltState.RUNNING;
886 |
887 | if ( wasRunning )
888 | TransferLogic.Halt( false );
889 |
890 | //GetRegs(); // Request registers from Unirom
891 | CPU.SetOneRegisterBE( reg_num, reg_value ); // Set the register
892 | CPU.SetRegs(); // Send registers to Unirom
893 |
894 | if ( wasRunning )
895 | TransferLogic.Cont( false );
896 |
897 | }
898 | SendGDBResponse( "OK" );
899 | }
900 | }
901 |
902 |
903 | ///
904 | /// Respond to GDB Write Registers 'G' packet
905 | ///
906 | ///
907 | private static void WriteRegisters( string data ) {
908 | uint length = (uint)data.Length - 1;
909 |
910 | lock ( SerialTarget.serialLock ) {
911 |
912 | bool wasRunning = CPU.GetHaltState() == CPU.HaltState.RUNNING;
913 |
914 | if ( wasRunning )
915 | TransferLogic.Halt( false );
916 |
917 | //GetRegs();
918 | for ( uint i = 0; i < length; i += 8 ) {
919 | uint reg_num = i / 8;
920 | uint reg_value = uint.Parse( data.Substring( (int)i + 1, 8 ), System.Globalization.NumberStyles.HexNumber );
921 | CPU.SetOneRegisterBE( reg_num, reg_value );
922 | }
923 | CPU.SetRegs();
924 |
925 | if ( wasRunning )
926 | TransferLogic.Cont( false );
927 |
928 | }
929 | }
930 | }
931 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU 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 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 |
7 | [Flags]
8 | public enum LogType : byte {
9 | Debug = (1 << 0),
10 | Info = (1 << 1),
11 | Warning = (1 << 2),
12 | Error = (1 << 3),
13 | // Set the Log type to Stream to avoid overriding any colours from
14 | // the incoming stream.
15 | Stream = (1 << 4),
16 | };
17 |
18 | public class Log {
19 |
20 |
21 | // All events enabled by default
22 | private static LogType SubscribedEvents = LogType.Debug | LogType.Info | LogType.Warning | LogType.Error | LogType.Stream;
23 |
24 | // buffered output
25 | private static ConsoleColor originalColor = Console.ForegroundColor; // not sure whether that works here
26 | private static byte[] byteCache = new byte[ 256 ];
27 | private static int byteCount = 0;
28 | private static int currentlyFlushing = 0;
29 | private static Timer flushTimer;
30 |
31 | public static void SetLevel(LogType logTypes) {
32 | SubscribedEvents = logTypes | LogType.Stream;
33 | }
34 |
35 | private static void SetColorByLogType( LogType inType ) {
36 |
37 | if ( inType == LogType.Stream ){
38 | return;
39 | }
40 |
41 | ConsoleColor logColor;
42 |
43 | switch ( inType ) {
44 | case LogType.Debug: logColor = ConsoleColor.DarkGreen; break;
45 | case LogType.Info: logColor = ConsoleColor.White; break;
46 | case LogType.Warning: logColor = ConsoleColor.DarkYellow; break;
47 | case LogType.Error: logColor = ConsoleColor.DarkRed; break;
48 | default: return;
49 | }
50 | Console.ForegroundColor = logColor;
51 | }
52 |
53 | //public static void Write( string inMessage = "", LogType inType = LogType.Info ) {
54 |
55 | // ConsoleColor originalColor = Console.ForegroundColor;
56 |
57 | // if ( (SubscribedEvents & inType) == 0 )
58 | // return;
59 |
60 | // SetColorByLogType( inType );
61 |
62 | // Console.Write( inMessage );
63 |
64 | // if ( (inType & LogType.Stream) == 0 ){
65 | // Console.ForegroundColor = originalColor;
66 | // }
67 | //}
68 |
69 | // buffered output version, force flushed on timeout (only Write for now)
70 | public static void Write( string inMessage = "", LogType inType = LogType.Info ) {
71 | if ( (SubscribedEvents & inType) == 0 )
72 | return;
73 |
74 | SetColorByLogType( inType );
75 |
76 | byte[] messageBytes = Encoding.ASCII.GetBytes( inMessage ); // hmm
77 | int messageLength = messageBytes.Length;
78 |
79 | while ( currentlyFlushing == 1 ) {
80 | Thread.Sleep( 1 );
81 | }
82 |
83 | for ( int i = 0; i < messageLength; i++ ) {
84 | byteCache[ byteCount++ ] = messageBytes[ i ];
85 |
86 | if ( byteCount == 256 ) {
87 | Console.Write( Encoding.ASCII.GetString( byteCache ) );
88 | byteCount = 0;
89 | }
90 | }
91 |
92 | if ( (inType & LogType.Stream) == 0 ) {
93 | Console.ForegroundColor = originalColor;
94 | }
95 |
96 | ResetFlushTimer();
97 | }
98 |
99 | private static void FlushBuffer( object state ) {
100 | if ( byteCount > 0 ) {
101 | currentlyFlushing = 1;
102 | Console.Write( Encoding.ASCII.GetString( byteCache, 0, byteCount ) );
103 | currentlyFlushing = 0;
104 | byteCount = 0;
105 | }
106 | }
107 |
108 | private static void ResetFlushTimer() {
109 | flushTimer?.Dispose(); // Dispose previous timer instance if it exists
110 |
111 | // force flush every 15 millis, stays a bit under a PSX vsync interval
112 | flushTimer = new Timer( FlushBuffer, null, 15, Timeout.Infinite );
113 | }
114 | // END AI stuff
115 |
116 | public static void WriteLine( string inMessage = "", LogType inType = LogType.Info ) {
117 |
118 | ConsoleColor originalColor = Console.ForegroundColor;
119 |
120 | if ( (SubscribedEvents & inType) == 0 )
121 | return;
122 |
123 | SetColorByLogType(inType);
124 |
125 | Console.WriteLine( inMessage );
126 |
127 | if ( (inType & LogType.Stream) == 0 ) {
128 | Console.ForegroundColor = originalColor;
129 | }
130 |
131 | }
132 |
133 | public static void TestMessage() {
134 | WriteLine( "Debug", LogType.Debug );
135 | WriteLine( "Info", LogType.Info );
136 | WriteLine( "Warning", LogType.Warning );
137 | WriteLine( "Error", LogType.Error );
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/PCDrv.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.IO.Ports;
6 |
7 | class PCDrv {
8 |
9 | public static TargetDataPort serial => Program.activeSerial;
10 |
11 | public enum PCDrvCodes { unknown, PCINIT_101 = 0x101, PCCREAT_102 = 0x102, PCOPEN_103 = 0x103, PCCLOSE_104 = 0x104, PCREAD_105 = 0x105, PCWRITE_106 = 0x106, PCSEEK_107 = 0x107 };
12 |
13 | public enum PCFileMode { READONLY, WRITEONLY, READWRITE };
14 |
15 | ///
16 | /// Grab the filename over sio
17 | /// Reads up to 255 chars or till it hits a null terminator
18 | ///
19 | ///
20 | private static string GetFilename() {
21 |
22 | string fileName = "";
23 |
24 | while ( true ) {
25 |
26 | if ( serial.BytesToRead > 0 ) {
27 |
28 | char inChar = (char)serial.ReadChar();
29 |
30 | if ( inChar == 0 ) {
31 | return fileName;
32 | } else {
33 | fileName += inChar;
34 | }
35 |
36 | if ( fileName.Length > 255 ) {
37 | Utils.Error( "Filename overflow!" );
38 | return "";
39 | }
40 |
41 | } // bytesToRead
42 |
43 | } // while
44 |
45 | } // local GetFileName
46 |
47 | ///
48 | /// To keep track of the stuff we've opened, closed, etc
49 | ///
50 | class PCFile {
51 |
52 | public FileStream fileStream;
53 | public string fileName;
54 | public UInt32 handle;
55 | public bool hasBeenClosed = false;
56 |
57 | // Much cleaner handling this here than in the .NET FileStreams
58 | public PCFileMode fileMode = PCFileMode.READWRITE;
59 |
60 | public override string ToString() {
61 | return $"PCFile: name={fileName}, handle={handle}, hasClosed={hasBeenClosed}, mode={fileMode}";
62 | }
63 |
64 | }
65 |
66 |
67 | private static List activeFiles = new List();
68 |
69 | ///
70 | /// Dumps some info related to known/tracked files
71 | ///
72 | public static void DumpTrackedFiles() {
73 | Log.WriteLine( "Tracked files: " );
74 | for ( int i = 0; i < activeFiles.Count; i++ ) {
75 | if ( activeFiles[ i ] != null ) Log.WriteLine( $"File {i} = {activeFiles[ i ]}" );
76 | }
77 | }
78 |
79 | ///
80 | /// Get a tracked file by filename,
81 | /// excludes files we've closed.
82 | ///
83 | /// the path
84 | /// a reference
85 | // Leaving these plain C style (vs linq) for readability
86 | private static PCFile GetOpenFile( string inFile ) {
87 | for ( int i = 0; i < activeFiles.Count; i++ ) {
88 |
89 | // not recycling handles
90 | if ( activeFiles[ i ].hasBeenClosed ) continue;
91 |
92 | if ( activeFiles[ i ].fileName.ToLowerInvariant() == inFile.ToLowerInvariant() ) {
93 | return activeFiles[ i ];
94 | }
95 | }
96 | return null;
97 | }
98 |
99 | ///
100 | /// Get a tracked file by handle
101 | /// excludes files we've closed
102 | ///
103 | /// a file opened via PCOpen/PCCreat
104 | /// a reference
105 | private static PCFile GetOpenFile( UInt32 inHandle ) {
106 | for ( int i = 0; i < activeFiles.Count; i++ ) {
107 |
108 | // not recycling handles
109 | if ( activeFiles[ i ].hasBeenClosed ) continue;
110 |
111 | if ( activeFiles[ i ].handle == inHandle ) {
112 | return activeFiles[ i ];
113 | }
114 |
115 | }
116 | return null;
117 | }
118 |
119 | ///
120 | /// Close the file stream and mark it closed
121 | /// handles are consecutive and will not be recycled
122 | ///
123 | ///
124 | ///
125 | private static bool ClosePCFile( UInt32 inHandle ) {
126 | for ( int i = 0; i < activeFiles.Count; i++ ) {
127 |
128 | // not recycling handles
129 | if ( activeFiles[ i ].hasBeenClosed ) continue;
130 |
131 | if ( activeFiles[ i ].handle == inHandle ) {
132 | activeFiles[ i ].hasBeenClosed = true;
133 | activeFiles[ i ].fileStream.Close();
134 | activeFiles[ i ].fileStream.Dispose();
135 | return true;
136 | }
137 |
138 | }
139 |
140 | Log.WriteLine( $"No active file with handle {inHandle} to close!" );
141 | return false;
142 |
143 | }
144 |
145 | ///
146 | /// Connect a file handle, filename and file stream
147 | ///
148 | /// the file name
149 | /// the handle
150 | /// the stream opened via PCCreate/PCOpen"/>
151 | private static void TrackFile( string inFile, UInt32 inHandle, FileStream inStream, PCFileMode inMode ) {
152 |
153 | // It's already tracked, open
154 | if ( GetOpenFile( inFile ) != null ) return;
155 |
156 | Log.WriteLine( $"Assigned file {inFile} with handle {inHandle}..." );
157 |
158 | PCFile p = new PCFile() {
159 | fileName = inFile,
160 | handle = inHandle,
161 | fileStream = inStream,
162 | fileMode = inMode
163 | };
164 | activeFiles.Add( p );
165 |
166 | }
167 |
168 | ///
169 | /// Grab a sequential handle, 1-indexed
170 | ///
171 | /// the next free handle
172 | private static UInt32 NextHandle() {
173 | return (UInt32)activeFiles.Count + 1;
174 | }
175 |
176 |
177 | ///
178 | /// The monitor recieved 0x00, 'p' ...
179 | /// Read the command ID bytes following that and process them.
180 | ///
181 | public static bool ReadCommand() {
182 |
183 | Log.WriteLine( "Got PCDRV ..." );
184 |
185 | // Wait till we get the PCDrv function code
186 | while ( serial.BytesToRead == 0 ) { }
187 | PCDrvCodes funcCode = (PCDrvCodes)TransferLogic.read32();
188 |
189 | Log.WriteLine( "Got function code: " + funcCode );
190 |
191 | // TODO: split these off into discrete functions?
192 |
193 | // PCInit
194 | if ( funcCode == PCDrvCodes.PCINIT_101 ) {
195 | serial.Write( "OKAY" );
196 | serial.Write( new byte[] { 0 }, 0, 1 );
197 | return true;
198 | }
199 |
200 | // PCCreat
201 | if ( funcCode == PCDrvCodes.PCCREAT_102 ) {
202 |
203 | // tell unirom to start with the filename, etc
204 | serial.Write( "OKAY" );
205 |
206 | string fileName = GetFilename();
207 | if ( fileName == "" ) {
208 | return false;
209 | }
210 | UInt32 parameters = TransferLogic.read32();
211 |
212 | bool isDir = ((parameters & 16) != 0);
213 |
214 | Log.WriteLine( $"PCCreat( {fileName}, {parameters} )" );
215 |
216 | PCFile pcFile = GetOpenFile( fileName );
217 |
218 | if ( pcFile != null ) {
219 | // We're already tracking this file, just return it's handle
220 | Log.WriteLine( "File already open, handle=" + pcFile.handle );
221 | serial.Write( "OKAY" );
222 | serial.Write( BitConverter.GetBytes( pcFile.handle ), 0, 4 );
223 | return true;
224 | }
225 |
226 | FileStream fStream;
227 | try {
228 |
229 | if ( isDir ) {
230 | throw new Exception( "Directories are not supported!" );
231 | } else {
232 | if ( !File.Exists( fileName ) ) {
233 | FileStream tempStream = File.Create( fileName );
234 | tempStream.Flush();
235 | tempStream.Close();
236 | tempStream.Dispose();
237 | } else {
238 | Log.WriteLine( $"File {fileName} already exists, using that..." );
239 | }
240 | fStream = new FileStream( fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite );
241 | }
242 |
243 | // open it read/write until otherwise specified via PCOpen
244 | UInt32 handle = NextHandle();
245 | TrackFile( fileName, handle, fStream, PCFileMode.READWRITE );
246 |
247 | serial.Write( "OKAY" );
248 | serial.Write( BitConverter.GetBytes( handle ), 0, 4 );
249 | return true;
250 |
251 | } catch ( Exception e ) {
252 | Log.WriteLine( $"Error creating file '{fileName}', ex={e}" );
253 | serial.Write( "NOPE" );
254 | return false;
255 | }
256 |
257 | } // PCCREAT_102
258 |
259 | // PCOpen
260 | if ( funcCode == PCDrvCodes.PCOPEN_103 ) {
261 |
262 | serial.Write( "OKAY" );
263 |
264 | string fileName = GetFilename();
265 | if ( fileName == "" ) {
266 | return false;
267 | }
268 |
269 | PCFileMode fileModeParams = (PCFileMode)TransferLogic.read32();
270 |
271 | Log.WriteLine( $"PCOpen( {fileName}, {fileModeParams} )" );
272 |
273 | PCFile f = GetOpenFile( fileName );
274 |
275 | if ( f != null ) {
276 |
277 | // just return the handle for this file...
278 |
279 | if ( f.fileMode != fileModeParams ) {
280 | Log.WriteLine( $"File {f.handle} already open, switching params to {fileModeParams}" );
281 | f.fileMode = fileModeParams;
282 | } else {
283 | Log.WriteLine( "File already open, handle=" + f.handle );
284 | }
285 |
286 | serial.Write( "OKAY" );
287 | serial.Write( BitConverter.GetBytes( f.handle ), 0, 4 );
288 | return true;
289 |
290 | }
291 |
292 | if ( !File.Exists( fileName ) ) {
293 | Log.WriteLine( "File doesn't exist!" );
294 | goto nope;
295 | }
296 |
297 | FileStream fs = null;
298 | try {
299 | fs = File.Open( fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite );
300 | } catch ( Exception e ) {
301 | Log.WriteLine( $"Error opening file '{fileName}', ex={e}" );
302 | goto nope;
303 | }
304 |
305 | UInt32 handle = NextHandle();
306 | TrackFile( fileName, handle, fs, fileModeParams );
307 | Log.WriteLine( "Returning file, handle=" + handle );
308 |
309 | serial.Write( "OKAY" );
310 | serial.Write( BitConverter.GetBytes( handle ), 0, 4 );
311 | return true;
312 |
313 |
314 | nope:;
315 | Log.WriteLine( "Failed.." );
316 | serial.Write( "NOPE" );
317 | return false;
318 |
319 | } // PCOPEN_103
320 |
321 | // PCClose
322 | if ( funcCode == PCDrvCodes.PCCLOSE_104 ) {
323 |
324 | serial.Write( "OKAY" );
325 |
326 | // unirom sends extra params to save kernel
327 | // space by grouping similar commands together.
328 | UInt32 handle = TransferLogic.read32();
329 | UInt32 unused1 = TransferLogic.read32();
330 | UInt32 unused2 = TransferLogic.read32();
331 |
332 | Log.WriteLine( $"PCClose( {handle} ) unusedParams: {unused1},{unused2}" );
333 |
334 | PCFile f = GetOpenFile( handle );
335 |
336 | try {
337 |
338 | if ( f == null ) {
339 | Log.WriteLine( "No such file... great success!" );
340 | serial.Write( "OKAY" ); // v0
341 | serial.Write( BitConverter.GetBytes( 0 ), 0, 4 ); // v1
342 | return true;
343 | } else {
344 | Log.WriteLine( $"Closing file {f.fileName} with handle {f.handle}..." );
345 | serial.Write( "OKAY" ); // v0
346 | serial.Write( BitConverter.GetBytes( f.handle ), 0, 4 ); // v1 let the garbage collector deal with it
347 | ClosePCFile( f.handle );
348 | return true;
349 | }
350 |
351 | } catch ( Exception e ) {
352 | Log.WriteLine( "Error closing file..." + e );
353 | serial.Write( "NOPE" ); // v0
354 | // don't need to send v1
355 | return false;
356 | }
357 |
358 |
359 |
360 | } // PCCLOSE_104
361 |
362 |
363 |
364 | // PCRead
365 | if ( funcCode == PCDrvCodes.PCREAD_105 ) {
366 |
367 | serial.Write( "OKAY" );
368 |
369 | // unirom sends extra params to save kernel
370 | // space by grouping similar commands together,
371 | // so 'memaddr' is for debugging only.
372 |
373 | // PCRead() takes (handle, buff*, len )
374 | // but interally passes it to _SN_Read as
375 | // ( 0, handle, len, buff* ), essentially
376 | // shuffling regs A0,A1,A2 into A1,A3,A2.
377 | // or just ( handle, len, buff* ). lol.
378 |
379 | UInt32 handle = TransferLogic.read32();
380 | int inLength = (int)TransferLogic.read32();
381 | UInt32 memaddr = TransferLogic.read32(); // not used, debugging only
382 |
383 | //Log.WriteLine( $"PCRead( {handle}, len={inLength}, dbg=0x{memaddr.ToString("X")} )" );
384 |
385 | PCFile pcFile = GetOpenFile( handle );
386 |
387 | Log.WriteLine( $"PCRead( {handle}, len=0x{inLength.ToString( "X" )} ); MemAddr=0x{memaddr.ToString( "X" )}, File={pcFile}" );
388 |
389 | if ( pcFile == null ) {
390 | Log.WriteLine( $"No file with handle 0x{handle.ToString( "X" )}, returning!" );
391 | serial.Write( "NOPE" ); // v0
392 | // don't need to send v1
393 | return false;
394 | } else {
395 | Log.WriteLine( "Reading file " + pcFile.fileName );
396 | }
397 |
398 | long streamLength = 0;
399 |
400 | try {
401 |
402 | FileStream fs = pcFile.fileStream;
403 |
404 | streamLength = fs.Length;
405 |
406 | byte[] bytes = new byte[ inLength ];
407 | int bytesRead = fs.Read( bytes, 0, inLength );
408 |
409 |
410 | if ( bytesRead <= 0 ) {
411 | //throw new Exception( "Read 0 bytes from the file.." );
412 | Log.WriteLine( "Warning - no bytes were read from the file - returning zeros..." );
413 | }
414 |
415 | // if we returned fewer bytes than requested, no biggie, the byte array is already set
416 |
417 | serial.Write( "OKAY" ); // v0
418 | serial.Write( BitConverter.GetBytes( bytes.Length ), 0, 4 ); // v1
419 |
420 | // Then
421 | UInt32 check = TransferLogic.CalculateChecksum( bytes, false );
422 | serial.Write( BitConverter.GetBytes( check ), 0, 4 );
423 | TransferLogic.WriteBytes( bytes, false, true );
424 |
425 | // again the weird order reflects a desire to save space within the psx kernel
426 | // and reuse some functions
427 |
428 | } catch ( Exception e ) {
429 | Log.WriteLine( $"Error reading file {pcFile.fileName} at pos={pcFile.fileStream.Position}, streamLength={streamLength} e={e}" );
430 | serial.Write( "NOPE" );
431 | return false;
432 | }
433 |
434 |
435 | } // PCREAD_105
436 |
437 | // PCWrite
438 | if ( funcCode == PCDrvCodes.PCWRITE_106 ) {
439 |
440 | serial.Write( "OKAY" );
441 |
442 | // PCWrite() takes (handle, buff*, len )
443 | // but interally passes it to _SN_Write as
444 | // ( 0, handle, len, buff* ), essentially
445 | // shuffling regs A0,A1,A2 into A1,A3,A2.
446 | // or just ( handle, len, buff* ). lol.
447 |
448 | UInt32 handle = TransferLogic.read32();
449 | int inLength = (int)TransferLogic.read32();
450 | UInt32 memaddr = TransferLogic.read32(); // not used, debugging only
451 |
452 | PCFile pcFile = GetOpenFile( handle );
453 |
454 | if ( pcFile == null ) {
455 | Log.WriteLine( $"No file with handle 0x{handle.ToString( "X" )}, returning!" );
456 | serial.Write( "NOPE" ); // v0
457 | // don't need to send v1
458 | return false;
459 | }
460 |
461 | Log.WriteLine( $"PCWrite( {handle}, len={inLength} ); fileName={pcFile.fileName} SourceAddr={memaddr.ToString( "X" )}, File={pcFile}" );
462 |
463 | if ( pcFile.fileMode == PCFileMode.READONLY ) {
464 | Log.WriteLine( "Error: File is readonly!" );
465 | serial.Write( "NOPE" );
466 | return false;
467 | }
468 | serial.Write( "OKAY" );
469 |
470 | try {
471 |
472 | FileStream fs = pcFile.fileStream;
473 |
474 | byte[] bytes = new byte[ inLength ];
475 | bool didRead = TransferLogic.ReadBytes_Raw( (UInt32)inLength, bytes );
476 |
477 | if ( !didRead ) {
478 | throw new Exception( "there was an error reading the stream from the psx!" );
479 | }
480 |
481 | Log.WriteLine( $"Read {inLength} bytes, flushing to {pcFile.fileName}..." );
482 |
483 | fs.Write( bytes, 0, inLength );
484 | fs.Flush( true );
485 |
486 | serial.Write( "OKAY" ); // v0
487 | serial.Write( BitConverter.GetBytes( bytes.Length ), 0, 4 ); // v1
488 |
489 | // again the weird order reflects a desire to save space within the psx kernel
490 | // and reuse some functions
491 |
492 | } catch ( Exception e ) {
493 | Log.WriteLine( $"Error writing file {pcFile.fileName}, streamLength={inLength} e={e}" );
494 | serial.Write( "NOPE" );
495 | return false;
496 | }
497 |
498 | return true;
499 |
500 | } //PCWRITE 106
501 |
502 | if ( funcCode == PCDrvCodes.PCSEEK_107 ) {
503 |
504 | serial.Write( "OKAY" );
505 |
506 | UInt32 handle = TransferLogic.read32();
507 | int seekPos = (int)TransferLogic.read32();
508 | SeekOrigin seekOrigin = (SeekOrigin)TransferLogic.read32();
509 |
510 | PCFile pcFile = GetOpenFile( handle );
511 |
512 | string fileName = pcFile != null ? pcFile.fileName : "";
513 | Log.WriteLine( $"PCSeek file {handle} to {seekPos}, type={seekOrigin}, fileName={fileName}" );
514 |
515 | if ( pcFile == null ) {
516 | Log.WriteLine( "Error: There is no file with handle 0x" + handle.ToString( "X" ) );
517 | serial.Write( "NOPE" );
518 | return false;
519 | }
520 |
521 | // Let's actually open the file and seek it to see if we bump into any issues
522 | try {
523 |
524 | FileStream fs = pcFile.fileStream;
525 | //fs.Seek( pcFile.filePointer, pcFile.seekMode );
526 | fs.Seek( seekPos, seekOrigin );
527 |
528 | Log.WriteLine( "Seeked position " + fs.Position );
529 |
530 | serial.Write( "OKAY" );
531 | serial.Write( BitConverter.GetBytes( fs.Position ), 0, 4 );
532 |
533 | } catch ( System.Exception e ) {
534 | Log.WriteLine( $"Exception when seeking file {handle}, e={e}" );
535 | serial.Write( "NOPE" );
536 | return false;
537 | }
538 |
539 | } //PCSEEK_107
540 |
541 |
542 | return true;
543 |
544 | }
545 |
546 | }
547 |
548 |
549 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | # NoPS - NotPsxSerial
5 | Aug 2022 - github.com/JonathanDotCel
6 |
7 | Serial transfer suite for Playstation 1 / Unirom
8 |
9 | # Links
10 |
11 | Discord: https://psx.dev
12 | Twitter: https://twitter.com/jonathandotcel
13 |
14 | More detailed instructions: https:///unirom.github.io
15 | (Including GDB, debugging, etc)
16 |
17 |
18 | # Features
19 |
20 | * Kernel-resident debugging
21 | Debug running games on a retail console
22 |
23 | * Cart/EEPROM flasher
24 | Back up and reflash cheat carts
25 |
26 | * .EXE or raw binary upload
27 | Run homebrew utilities or upload assets for development
28 |
29 | * Memorycard tools
30 | Backup or rewrite whole memory cards to raw (emulator) format
31 |
32 | * Peeks n Pokes
33 | Peek or poke running games/homebrew (incl cheat codes)
34 |
35 | * Mem dumps
36 | Grab a copy of RAM, cart EEPROM, BIOS, etc
37 |
38 | * TCP->SIO Bridge
39 | Raw communication via TCP
40 |
41 | * In progress: GDB TCP->SIO bridge!
42 | Step through your code in the editor, etc
43 |
44 | ## Upgrading to UniROM:
45 |
46 | You can use nops to upgrade a cart already running Unirom 8 or higher.
47 | Instructions in the unirom download/github: http://github.com/JonathanDotCel
48 |
49 | ## Supported configs:
50 |
51 | ### Cables:
52 |
53 | * Basic 3 wire TX/RX/GND
54 | (e.g. a 3 quid dongle off amazon and a couple of wires)
55 | * Sharklink with handshake
56 | * Yaroze cables
57 | * Switchable cables
58 |
59 |
60 | ### OS:
61 | * Windows via .NET or Mono
62 | * OSX / Linux via Mono
63 |
64 |
65 | ## Basic Usage
66 |
67 | The hello world test executable was written by Shadow of PSXDev.net.
68 | To run it:
69 | * `nops /exe psx_helloworld.exe COM8`
70 |
71 | Where COMPORT might be COM3, COM14, etc under windows and /dev/ttyWhatever under *nix with mono.
72 | win: Find it by opening `devmgmt.msc` from the run box.
73 | mac + linux: run ./nops for some example device names
74 |
75 | e.g.
76 | * `nops /rom unirom_s.rom COM14`
77 | * `./nops /rom unirom_s.rom /dev/ttyUSB01`
78 |
79 | Once you've specified a com port in this directory, it will be saved to `COMPORT.TXT`
80 |
81 | so next time around you just need to:
82 |
83 | * `nops /rom unirom_s.rom`
84 |
85 | ### Uploading Stuff
86 |
87 | Your basic "Upload to the PSX" features are:
88 |
89 | * `nops /exe whatever.exe`
90 | * `nops /rom whatever.rom`
91 | * `nops /bin 0x8000ADDR somefile.whatever`
92 |
93 | Where /exe executs a file from the header information, /rom will attempt to identify the JEDEC chip and flash it, and /bin will just upload a binary file to a specific address.
94 |
95 | ### Flow Control
96 |
97 | To jump to an address
98 | * `nops /jump 0x80?????? `
99 |
100 | To call an address (and return)
101 | * `nops /call 0x80?????? `
102 |
103 | To poke an 8, 16 or 32 bit value
104 | * `nops /poke8 0x80100000 0x01`
105 | * `nops /poke16 0x801F0012 0xFFEE`
106 | * `nops /poke32 0xA000C000 0x00112233`
107 |
108 | ### Reading data
109 |
110 | And to get the information back:
111 | * ` nops /dump 0x80???? 0xSIZE`
112 |
113 | Or watch it oncreen:
114 | * `nops /watch 0x80030000 0xSIZE`
115 |
116 | ### Misc
117 |
118 | returns PONG!
119 | * `nops /ping `
120 |
121 | Restart the machine
122 | * `nops /reset`
123 |
124 |
125 | ### Other operators:
126 |
127 | `/m`
128 | Use in conjunction with anything else to keep the serial connection open and view printf/TTY logs:
129 | E.g.
130 | * `nops /exe whatever.exe /m`
131 | * `nops /rom whatever.rom /m`
132 |
133 | or simply
134 | `nops /m`
135 | if you just want to listen.
136 |
137 | ### /fast and /slow
138 |
139 | Every command also supports `/fast`
140 | This will send a low speed ping to the PSX tell it to bump from 115200 to 518400
141 |
142 | Note:
143 | Once in `/fast` mode, the next time you run nops, it has no way to tell
144 | So remember to include `/fast` in every command till you restart!
145 |
146 |
147 | # Debug operators:
148 |
149 | ### This is just the list - Examples below.
150 |
151 | The debug functions let you use nops while a game or homebrew is running.
152 | It copies a small SIO/Debug handler into unused kernel memory and remains resident through gameplay.
153 |
154 | Enter debug mode one of these ways:
155 | * `L1+Square`
156 | * `nops /debug`
157 |
158 | ## The "Halt State"
159 |
160 | There's 3 ways to enter the halt state and they all behave the same way.
161 |
162 | * You did `nops /halt`
163 | * The game crashed
164 | * A breakpoint/hook triggered
165 |
166 | You can continue to talk to the playstation.
167 | To exit: `nops /cont`
168 |
169 |
170 | ### Note:
171 | You won't see the exception handler if the game crashes in debug mode.
172 | You'll be notified over serial though, so have `/m` ready to catch it!
173 | `nops /cont` returns from this.
174 | If the gods are on your side, it even recovers from some crashes.
175 |
176 | #### Registers
177 |
178 | This shows the registers as they were the instant the machine halted.
179 | * `nops /regs `
180 |
181 | Example: changing a register's value
182 |
183 | * `nops /setreg v0 0x1F801800`
184 | * `nops /setreg pc 0xBFC00000` *
185 |
186 | * this one for example will restart the system when you `nops /cont`
187 |
188 | ### Note:
189 | `/setreg` will only really work in the halt state!
190 |
191 | ### Hooks
192 |
193 | Enter the halt state on read, write, or execution
194 |
195 | * `nops /hookread 0xADDR`
196 | * `nops /hookwrite 0xADDR`
197 | * `nops /hookex 0xADDR`
198 |
199 | One hook at a time!
200 |
201 |
202 | # Debug Command Examples
203 |
204 | Note: GDB bridge in progress!
205 |
206 | As you shoul already know (because you read the topic on debug commands, right?) start with:
207 | * ` nops /debug`
208 | * or `L1 + Square`
209 |
210 | An int-driven SIO handler has now been installed into the kernel, and can talk to nops once you start a game, etc.
211 | The command set is basically the same as for regular SIO.
212 |
213 | ## Example: Breakpoint on a particular location
214 |
215 | Yo can do these two in whatever order:
216 | `nops /debug`
217 | `nops /bin 0x80030000 something.bin`
218 |
219 | Now apply the hook
220 | `nops /hookex 0x80031234`
221 |
222 | And execute the binary
223 | `nops /jal 0x80030000 /m`
224 |
225 | ### Hint!
226 | Don't forget the `/m`
227 | This will put nops into `monitor mode`.
228 | Monitor mode will detect when the system has halted, and offer a debug menu.
229 |
230 | ## Example: Memory breakpoint on an exe
231 |
232 | Again, you have some flexibility here - but you'd best enter debug mode before uploading the .exe!
233 |
234 | `nops /debug`
235 | `nops /hookread 0x80041234`
236 | `nops /exe myfile.exe`
237 |
238 | or if you want to go a bit faster:
239 |
240 | `nops /fast /debug`
241 | `nops /fast /hookread 0x80041234`
242 | `nops /fast /exe myfile.exe`
243 |
244 |
245 | ### Hint!
246 | You've specified `/fast`. Next time you start nops, it has no way of knowing if the ps is in fast mode or not.
247 | So remember to use `/fast` on every command... or not at all!
248 | You can switch back and forth in most cases with the following (If you're sick of typing it).
249 | * `nops /fast`
250 | * `nops /slow`
251 | * `Square`
252 |
253 | ## I'm looking at the regs, now what?
254 |
255 | To continue from a `nops /halt`` or a crash
256 | `/nops /cont`
257 |
258 | I need to see the regs again!
259 | `/nops /regs`
260 |
261 | Okay, how do I change them?
262 | `nops /setreg v0 0x05`
263 |
264 |
265 |
266 | # TCP Bridge
267 |
268 | Not a serial person?
269 | Open a TCP bridge on port 3333
270 | * `nops /bridge 3333`
271 |
272 | Connect via (e.g.)
273 | `telnet localhost 3333`
274 |
275 | You can now type raw commands and send raw bits n bytes through the serial port.
276 |
277 | `REST`
278 | to reset the system
279 |
280 | `PING`
281 | PONG!
282 |
283 | In Progress:
284 |
285 | `nops /GDB`
286 | Like `nops /bridge` but runs `nops /debug` first.
287 |
288 |
289 | ## A big thanks:
290 |
291 | While NoPS has been re-written from the ground up for Unirom 8, before that it was
292 | a decompiled version of Shadow's PSXSerial with the header changed to reflect that.
293 | (Or more specifically to deny that). In keeping with what I've just decided is tradition,
294 | we're going with the same motif.
295 |
296 |
297 |
298 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | Open README.md in notepad.
--------------------------------------------------------------------------------
/SerialTarget.cs:
--------------------------------------------------------------------------------
1 | //
2 | // For connections through a local device (COM14, /dev/tty.SLAB_USBtoUART, etc)
3 | //
4 |
5 | using System.IO.Ports;
6 |
7 | public class SerialTarget : TargetDataPort
8 | {
9 | private static SerialPort properSerial;
10 |
11 | // barrier to prevent the monitor going nuts and
12 | // eating our serial data when we're trying to do
13 | // comms on another thread, initiated by socket callbacks
14 | public static object serialLock = new object();
15 | protected SIOSPEED connectionType;
16 |
17 | public SerialTarget(string portName, SIOSPEED connectionType, int baudRate, Parity parity, int dataBits, StopBits stopBits)
18 | : base(portName, connectionType, baudRate, parity, dataBits, stopBits)
19 | {
20 | properSerial = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
21 | this.connectionType = connectionType;
22 | }
23 |
24 | public override int BytesToRead
25 | {
26 | get { return properSerial.BytesToRead; }
27 | }
28 |
29 | public override int BytesToWrite
30 | {
31 | get { return properSerial.BytesToWrite; }
32 | }
33 |
34 | public override Handshake Handshake
35 | {
36 | get { return properSerial.Handshake; }
37 | set { properSerial.Handshake = value; }
38 | }
39 |
40 | public override bool DtrEnable
41 | {
42 | get { return properSerial.DtrEnable; }
43 | set { properSerial.DtrEnable = value; }
44 | }
45 | public override bool RtsEnable
46 | {
47 | get { return properSerial.RtsEnable; }
48 | set { properSerial.RtsEnable = value; }
49 | }
50 |
51 | public override int ReadTimeout
52 | {
53 | get { return properSerial.ReadTimeout; }
54 | set { properSerial.ReadTimeout = value; }
55 | }
56 | public override int WriteTimeout
57 | {
58 | get { return properSerial.WriteTimeout; }
59 | set { properSerial.WriteTimeout = value; }
60 | }
61 |
62 | public override bool SkipAcks => connectionType == SIOSPEED.FTDI;
63 |
64 | public override void Open()
65 | { properSerial.Open(); }
66 |
67 | public override void Close()
68 | { properSerial.Close(); }
69 |
70 | public override int ReadByte()
71 | { return properSerial.ReadByte(); }
72 |
73 | public override int ReadChar()
74 | { return properSerial.ReadChar(); }
75 |
76 | public override void Write(string text)
77 | { properSerial.Write(text); }
78 |
79 | public override void Write(char[] buffer, int offset, int count)
80 | { properSerial.Write(buffer, offset, count); }
81 |
82 | public override void Write(byte[] buffer, int offset, int count)
83 | { properSerial.Write(buffer, offset, count); }
84 | }
--------------------------------------------------------------------------------
/TCPTarget.cs:
--------------------------------------------------------------------------------
1 | //
2 | // For targeting a TCP client connnected to the serial port.
3 | //
4 | // E.g. an ESP32 on wifi, which pushes the raw bytes back
5 | // and forth through the serial port.
6 | //
7 | // E.g. nops in TCP<->Serial bridge mode on another machine
8 | //
9 | // E.g. an emulator with a local port open which speaks nops
10 | //
11 |
12 | using System;
13 | using System.IO.Ports;
14 | using System.Net;
15 | using System.Net.Sockets;
16 | using System.Text;
17 | using System.Threading;
18 |
19 | public class RingBuffer
20 | {
21 | // Adapted from https://gist.github.com/ryankurte/61f95dc71133561ed055ff62b33585f8
22 | public byte[] Buffer;
23 | uint head;
24 | uint tail;
25 | uint size;
26 |
27 | public RingBuffer(uint _size)
28 | {
29 | this.size = _size;
30 | this.Buffer = new byte[_size];
31 | }
32 |
33 | public long BytesAvailable()
34 | {
35 | return (this.size - this.tail + this.head) % this.size;
36 | }
37 |
38 | public void Discard() {
39 | this.head = 0;
40 | this.tail = 0;
41 | }
42 |
43 | public int Read()
44 | {
45 | byte temp;
46 | if (this.tail == this.head)
47 | {
48 | // Empty - bytes not written?
49 | Log.WriteLine("Underflow condition tail == head on read(), Head = " + this.head + " Tail = " + tail, LogType.Error);
50 | return -1;
51 | }
52 |
53 | temp = this.Buffer[this.tail];
54 |
55 | this.tail = (this.tail + 1) % this.size;
56 | return temp;
57 | }
58 |
59 | public int Write(byte data)
60 | {
61 | if (((this.head + 1) % this.size) == this.tail)
62 | {
63 | // Buffer full
64 | Log.WriteLine("Overflow conditon, Head == tail on write(" + data + ")", LogType.Error);
65 | return -1;
66 | }
67 |
68 | this.Buffer[head] = data;
69 | this.head = (this.head + 1) % this.size;
70 |
71 | return 0;
72 | }
73 |
74 | public void Reset()
75 | {
76 | head = 0;
77 | tail = 0;
78 | }
79 | }
80 |
81 | public class TCPTarget : TargetDataPort
82 | {
83 | private static Socket socket;
84 | public const int socketBufferSize = 1024 * 2;
85 | public const int ringBufferSize = 1024 * 4;
86 | public static byte[] socketBuffer_Receive = new byte[socketBufferSize];
87 |
88 | public RingBuffer ringBuffer_Receive = new RingBuffer(ringBufferSize);
89 | protected SIOSPEED connectionType;
90 |
91 | override public int BytesToRead
92 | {
93 | get
94 | {
95 | return (int)ringBuffer_Receive.BytesAvailable();
96 | }
97 | }
98 |
99 | private volatile int _bytestowrite = 0;
100 | override public int BytesToWrite
101 | {
102 | get
103 | {
104 | return _bytestowrite;
105 | }
106 | }
107 |
108 | // Unused place-holders, counter-parts to serial properties used in program
109 | override public Handshake Handshake { get; set; }
110 | override public bool DtrEnable { get; set; }
111 | override public bool RtsEnable { get; set; }
112 |
113 | override public int ReadTimeout
114 | {
115 | get { return socket.ReceiveTimeout; }
116 | // To-do: Move this hacky fix to the port creation
117 | set { socket.ReceiveTimeout = value * 2; }
118 | }
119 |
120 | override public int WriteTimeout
121 | {
122 | get { return socket.SendTimeout; }
123 | // To-do: Move this hacky fix to the port creation
124 | set { socket.SendTimeout = value * 2; }
125 | }
126 |
127 | static IPEndPoint remoteEndpoint;
128 | static IPAddress ip;
129 |
130 | public override bool SkipAcks => connectionType == SIOSPEED.FTDI;
131 |
132 | public override void Open()
133 | {
134 | try
135 | {
136 | Log.WriteLine("Opening remote connection to " + ip + ":" + remoteEndpoint.Port);
137 | socket.Connect(remoteEndpoint);
138 |
139 | // Give the accepting socket time to accept the connection
140 | // The reset command fires off and closes the connection especially quickly
141 | Thread.Sleep(100);
142 | }
143 | catch (SocketException e)
144 | {
145 | Console.WriteLine(e.Message);
146 | }
147 | catch (Exception e)
148 | {
149 | Log.WriteLine("Source : " + e.Source, LogType.Error);
150 | Log.WriteLine("Message : " + e.Message, LogType.Error );
151 | }
152 |
153 | if (socket.Connected)
154 | {
155 | Log.WriteLine("Connected to " + ip + ":" + remoteEndpoint.Port);
156 | Log.WriteLine("Starting async receive task");
157 | socket.BeginReceive(socketBuffer_Receive, 0, socketBufferSize, 0, new AsyncCallback(RecieveCallback), socket);
158 | }
159 | else
160 | {
161 | throw new System.Exception("Failed to connect to " + ip + ":" + remoteEndpoint.Port);
162 | }
163 | }
164 |
165 | public override void Close()
166 | {
167 | // Try to give the tcp thread some time to cleanly finish transmission
168 | // The reset command fires off and closes the connection especially quickly
169 | Thread.Sleep( 100 );
170 |
171 | socket.Close();
172 | this._bytestowrite = 0;
173 | this.ringBuffer_Receive.Reset();
174 | }
175 |
176 | public override int ReadByte()
177 | {
178 | int temp = 0;
179 |
180 | if (this.ringBuffer_Receive.BytesAvailable() == 0)
181 | {
182 | DateTime delay_start_time = DateTime.Now;
183 | DateTime delay_end_time = delay_start_time.AddMilliseconds(this.ReadTimeout);
184 |
185 | while (this.ringBuffer_Receive.BytesAvailable() == 0)
186 | {
187 | if (DateTime.Now >= delay_end_time)
188 | {
189 | Log.WriteLine("ReadByte() timeout\n", LogType.Error);
190 | return -1;
191 | }
192 | }
193 | }
194 |
195 | temp = this.ringBuffer_Receive.Read();
196 |
197 | if (temp < 0)
198 | {
199 | Log.WriteLine("ReadByte() temp < 0 with " + this.ringBuffer_Receive.BytesAvailable() + "bytes to read", LogType.Error);
200 | this.ringBuffer_Receive.Discard(); // Program closes after this but might as well tidy up
201 | return 0;
202 | }
203 | else
204 | {
205 | return Convert.ToByte(temp);
206 | }
207 | }
208 |
209 | public override int ReadChar()
210 | {
211 | int temp = 0;
212 |
213 | if ( this.ringBuffer_Receive.BytesAvailable() == 0)
214 | {
215 | DateTime delay_start_time = DateTime.Now;
216 | DateTime delay_end_time = delay_start_time.AddMilliseconds(this.ReadTimeout);
217 |
218 | while ( this.ringBuffer_Receive.BytesAvailable() == 0)
219 | {
220 | if (DateTime.Now >= delay_end_time)
221 | {
222 | Log.WriteLine("ReadChar() timeout\n", LogType.Error);
223 | return -1;
224 | }
225 | }
226 | }
227 |
228 | temp = this.ringBuffer_Receive.Read();
229 |
230 | if (temp < 0)
231 | {
232 | Log.WriteLine("ReadChar() temp < 0 with " + this.ringBuffer_Receive.BytesAvailable() + "bytes to read", LogType.Error);
233 | this.ringBuffer_Receive.Discard(); // Program closes after this but might as well tidy up
234 | return 0;
235 | }
236 | else
237 | {
238 | return Convert.ToChar(temp);
239 | }
240 | }
241 |
242 | public override void Write(string text)
243 | {
244 | this._bytestowrite += text.Length;
245 | int bytes_written = socket.Send(Encoding.ASCII.GetBytes(text));
246 | this._bytestowrite -= bytes_written;
247 |
248 | if (bytes_written != text.Length)
249 | {
250 | Log.WriteLine("socket.Send mismatched byte count, wrote " + bytes_written + " expected " + text.Length, LogType.Error);
251 | }
252 | }
253 |
254 | public override void Write(char[] buffer, int offset, int count)
255 | {
256 | this._bytestowrite += count;
257 | int bytes_written = socket.Send(Encoding.ASCII.GetBytes(buffer, offset, count));
258 | this._bytestowrite -= bytes_written;
259 |
260 | if (bytes_written != count)
261 | {
262 | Log.WriteLine("socket.Send mismatched byte count, wrote " + bytes_written + " expected " + count, LogType.Error);
263 | }
264 | }
265 |
266 | public override void Write(byte[] buffer, int offset, int count)
267 | {
268 | this._bytestowrite += count;
269 | int bytes_written = socket.Send(buffer, offset, count, SocketFlags.None);
270 | this._bytestowrite -= bytes_written;
271 |
272 | if(bytes_written != count)
273 | {
274 | Log.WriteLine("socket.Send mismatched byte count, wrote " + bytes_written + " expected " + count, LogType.Error);
275 | }
276 |
277 | }
278 |
279 | public TCPTarget(string remoteHost, UInt32 remotePort, SIOSPEED connectionType ) : base(remoteHost, remotePort, connectionType)
280 | {
281 | socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
282 | ip = IPAddress.Parse(remoteHost);
283 | remoteEndpoint = new IPEndPoint(ip, (int)remotePort);
284 | this.connectionType = connectionType;
285 | }
286 |
287 | private void RecieveCallback(IAsyncResult ar)
288 | {
289 | try
290 | {
291 | Socket recvSocket = (Socket)ar.AsyncState;
292 | int numBytesRead = recvSocket.EndReceive(ar);
293 | if(numBytesRead >= socketBufferSize)
294 | {
295 | Log.WriteLine("More bytes received than buffer can hold: " + numBytesRead, LogType.Error);
296 | }
297 |
298 | if (numBytesRead > 0)
299 | {
300 | for (int i = 0; i < numBytesRead; i++)
301 | {
302 | if (this.ringBuffer_Receive.Write(Convert.ToByte(socketBuffer_Receive[i])) < 0)
303 | {
304 | Log.WriteLine("buffer.Write() < 0", LogType.Error);
305 | return;
306 | }
307 | }
308 | recvSocket.BeginReceive(socketBuffer_Receive, 0, socketBufferSize, 0, new AsyncCallback(RecieveCallback), recvSocket);
309 | }
310 | else
311 | {
312 | Log.WriteLine("Read 0 bytes...", LogType.Error);
313 | }
314 | }
315 | catch (ObjectDisposedException)
316 | {
317 |
318 | }
319 | catch (Exception e)
320 | {
321 | Log.WriteLine("Source : " + e.Source, LogType.Error);
322 | Log.WriteLine("Message : " + e.Message, LogType.Error);
323 | }
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/TargetDataPort.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Base class for e.g. Serial and TCP targets
3 | //
4 | // "Target" to distinguish between e.g. a remote TCP target and a local listen server for TCP<->UART bridge
5 | //
6 |
7 | using System;
8 | using System.IO.Ports;
9 |
10 | abstract public class TargetDataPort
11 | {
12 | public TargetDataPort(string portName, SIOSPEED connectionType, int baudRate, Parity parity, int dataBits, StopBits stopBits) { }
13 | public TargetDataPort(string remoteHost, UInt32 remotePort, SIOSPEED connectionType ) { }
14 |
15 | abstract public int BytesToRead { get; }
16 | abstract public int BytesToWrite { get; }
17 | abstract public Handshake Handshake { get; set; }
18 | abstract public bool DtrEnable { get; set; }
19 | abstract public bool RtsEnable { get; set; }
20 | abstract public int ReadTimeout { get; set; }
21 | abstract public int WriteTimeout { get; set; }
22 |
23 | abstract public bool SkipAcks{ get; }
24 |
25 | abstract public void Open();
26 | abstract public void Close();
27 | abstract public int ReadByte();
28 | abstract public int ReadChar();
29 | abstract public void Write(string text);
30 | abstract public void Write(char[] buffer, int offset, int count);
31 | abstract public void Write(byte[] buffer, int offset, int count);
32 | }
--------------------------------------------------------------------------------
/Utils.cs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | using System;
6 | using System.IO;
7 | using System.Runtime.InteropServices;
8 |
9 | public class Utils {
10 |
11 | ///
12 | /// Grabs a 32 bit hex value from standard input
13 | ///
14 | public static UInt32 ParseHexa( CommandMode inCommand, string inString ) {
15 |
16 | string iLower = inString.ToLowerInvariant();
17 | iLower = iLower.Replace( inCommand.command().ToLowerInvariant(), "" );
18 |
19 | #if DebugArgs
20 | Log.WriteLine( "Parsing hexa " + inString, LogType.Debug );
21 | #endif
22 |
23 | // Whatever's left should be the address
24 | UInt32 outAddr = (UInt32)Convert.ToInt32( iLower, 16 );
25 |
26 | Log.Write( " - Parsed hexa: 0x" + outAddr.ToString( "X8" ) + "\n", LogType.Debug );
27 |
28 | return outAddr;
29 |
30 | }
31 |
32 | ///
33 | /// Red console text + returns false; a standard error response.
34 | ///
35 | /// returns false so you can do return Error("blah");
36 | public static bool Error( string inString, bool printHeader = true ) {
37 |
38 | if ( printHeader )
39 | Program.PrintUsage( false );
40 |
41 | Console.ForegroundColor = ConsoleColor.Red;
42 | Console.Write( "\n\n" );
43 | Console.Write( "ERROR! " + inString + " \n \n " );
44 |
45 | // Leaves the user with a green console.
46 | // Because we can. Shh, don't tell them till they get it right.
47 | Console.ForegroundColor = ConsoleColor.Green;
48 |
49 | return false;
50 |
51 | }
52 |
53 | private static bool hasCachedDefaultConsoleColour = false;
54 | private static ConsoleColor originalColour = ConsoleColor.Black;
55 |
56 | ///
57 | /// Windows: sets the default console colour
58 | /// Mac: the terminal might be white, so cache whatever's already there
59 | ///
60 | public static void SetDefaultColour() {
61 |
62 | if ( !hasCachedDefaultConsoleColour ) {
63 | hasCachedDefaultConsoleColour = true;
64 | originalColour = Console.ForegroundColor;
65 | }
66 |
67 | Console.ForegroundColor = originalColour;
68 |
69 | }
70 |
71 | public static TimeSpan GetSpan() {
72 | return (DateTime.UtcNow - new DateTime( 1970, 1, 1 )); // shortest way to represent the epoch?
73 | }
74 |
75 | public static PlatformID realPlatform {
76 | get {
77 |
78 | PlatformID deviceID = Environment.OSVersion.Platform;
79 |
80 | // Bug:
81 | // Mono always returns Unix (even for version numbers) when running on OSX
82 | // even though the correct enum exists.
83 |
84 | if ( Directory.Exists( "/Applications" ) & Directory.Exists( "/Volumes" ) )
85 | deviceID = PlatformID.MacOSX;
86 |
87 | return deviceID;
88 |
89 | }
90 | }
91 |
92 | public static bool inWindowsPlatform {
93 | get {
94 | PlatformID plat = realPlatform;
95 | if ( plat == PlatformID.MacOSX || plat == PlatformID.Unix || plat == (PlatformID)128 ){
96 | return false;
97 | }
98 | // Missing anything? Not sure what to do for xbox, lol
99 | return true;
100 | }
101 | }
102 |
103 | #region virtual processing
104 |
105 | [DllImport( "kernel32.dll", SetLastError = true )]
106 | private static extern bool SetConsoleMode( IntPtr hConsoleHandle, uint dwMode );
107 |
108 | [DllImport( "kernel32.dll", SetLastError = true )]
109 | static extern bool GetConsoleMode( IntPtr hConsoleHandle, out uint lpMode );
110 |
111 | [DllImport( "kernel32.dll", SetLastError = true )]
112 | static extern IntPtr GetStdHandle( int nStdHandle );
113 |
114 | [Flags]
115 | private enum ConsoleOutputModes : uint {
116 | ENABLE_PROCESSED_OUTPUT = 0x0001,
117 | ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
118 | ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
119 | DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
120 | ENABLE_LVB_GRID_WORLDWIDE = 0x0010
121 | }
122 |
123 | private const int STD_INPUT_HANDLE = -10;
124 | private const int STD_OUTPUT_HANDLE = -11;
125 | private const int STD_ERROR_HANDLE = -12;
126 | private const int INVALID_HANDLE_VALUE = -1;
127 |
128 | public static void ApplyConsoleMode() {
129 |
130 | if ( !inWindowsPlatform ) return;
131 |
132 | IntPtr consoleHandle = GetStdHandle( STD_OUTPUT_HANDLE );
133 | if ( consoleHandle.ToInt32() == -INVALID_HANDLE_VALUE ) {
134 | Log.WriteLine( "Error: invalid stdout handle value", LogType.Error );
135 | return;
136 | }
137 |
138 | UInt32 mode = 0;
139 | if ( !GetConsoleMode( consoleHandle, out mode ) ) {
140 | Log.WriteLine( "Error: couldn't read console mode...", LogType.Error );
141 | return;
142 | }
143 |
144 | mode |= (UInt32)ConsoleOutputModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
145 |
146 | SetConsoleMode( consoleHandle, mode );
147 |
148 | Log.WriteLine( $"Applied console mode: 0x{mode.ToString( "X" )}", LogType.Debug );
149 |
150 | }
151 |
152 | #endregion virtual processing
153 |
154 | }
155 |
156 |
--------------------------------------------------------------------------------
/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/buildme.bat:
--------------------------------------------------------------------------------
1 | cls
2 |
3 | REM Old way, no elfsharp:
4 | REM
5 | REM Both use the microsoft CSC under windows, so it doesn't really matter
6 | REM Wherever csc.exe is, either in your Vis Studio folder, win folder, mono, etc
7 | REM set path=%PATH%;c:\windows\Microsoft.NET\Framework\blahblahlbah
8 | REM set path=%PATH%;C:\ins\cs2017comm\MSBuild\15.0\Bin\Roslyn
9 | REM set path=%PATH%;C:\Program Files (x86)\Mono\bin\
10 | REM cls
11 | REM del NOTPSXSERIAL.EXE
12 | REM del NoPS.EXE
13 | REM pause
14 | REM csc TransferLogic.cs NOTPSXSERIAL.cs GDB.cs Utils.cs -out:nops.exe
15 | REM
16 |
17 |
18 | REM This chunk is shamelessly ripped from yoyo's excellent SO post:
19 | REM https://stackoverflow.com/questions/328017/path-to-msbuild
20 |
21 | REM TODO: find the command line version on windows.
22 | set msbuild.exe=
23 | for /D %%D in (%SYSTEMROOT%\Microsoft.NET\Framework\v4*) do set msbuild.exe=%%D\MSBuild.exe
24 |
25 | if not defined msbuild.exe echo error: can't find MSBuild.exe & goto :eof
26 | if not exist "%msbuild.exe%" echo error: %msbuild.exe%: not found & goto :eof
27 |
28 | %msbuild% nops_sln.sln
29 |
30 | pause
31 |
32 |
--------------------------------------------------------------------------------
/buildnops.bat:
--------------------------------------------------------------------------------
1 | cls
2 |
3 | REM Old way, no elfsharp:
4 | REM
5 | REM Both use the microsoft CSC under windows, so it doesn't really matter
6 | REM Wherever csc.exe is, either in your Vis Studio folder, win folder, mono, etc
7 | REM set path=%PATH%;c:\windows\Microsoft.NET\Framework\blahblahlbah
8 | REM set path=%PATH%;C:\ins\cs2017comm\MSBuild\15.0\Bin\Roslyn
9 | REM set path=%PATH%;C:\Program Files (x86)\Mono\bin\
10 | REM cls
11 | REM del NOTPSXSERIAL.EXE
12 | REM del NoPS.EXE
13 | REM pause
14 | REM csc TransferLogic.cs NOTPSXSERIAL.cs GDB.cs Utils.cs -out:nops.exe
15 | REM
16 |
17 |
18 | REM This chunk is shamelessly ripped from yoyo's excellent SO post:
19 | REM https://stackoverflow.com/questions/328017/path-to-msbuild
20 |
21 | REM TODO: find the command line version on windows.
22 | set msbuild.exe=
23 | for /D %%D in (%SYSTEMROOT%\Microsoft.NET\Framework\v4*) do set msbuild.exe=%%D\MSBuild.exe
24 |
25 | if not defined msbuild.exe echo error: can't find MSBuild.exe & goto :eof
26 | if not exist "%msbuild.exe%" echo error: %msbuild.exe%: not found & goto :eof
27 |
28 | %msbuild% nops_sln.sln
29 |
30 | pause
31 |
32 |
--------------------------------------------------------------------------------
/buildnops.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | clear
4 |
5 | rm NOTPSXSERIAL.EXE
6 | rm NoPS.EXE
7 | rm nops.exe
8 |
9 | # Old way, no elfsharp
10 | # csc TransferLogic.cs NOTPSXSERIAL.cs GDB.cs Utils.cs -out:nops.exe
11 |
12 | msbuild nops_sln.sln
13 |
14 |
15 |
--------------------------------------------------------------------------------
/elfsharp_license.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Konrad Kruczyński and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | This software uses ELF machine constants from the LLVM projects, whose license
23 | is provided below:
24 |
25 | ==============================================================================
26 | LLVM Release License
27 | ==============================================================================
28 | University of Illinois/NCSA
29 | Open Source License
30 |
31 | Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign.
32 | All rights reserved.
33 |
34 | Developed by:
35 |
36 | LLVM Team
37 |
38 | University of Illinois at Urbana-Champaign
39 |
40 | http://llvm.org
41 |
42 | Permission is hereby granted, free of charge, to any person obtaining a copy of
43 | this software and associated documentation files (the "Software"), to deal with
44 | the Software without restriction, including without limitation the rights to
45 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
46 | of the Software, and to permit persons to whom the Software is furnished to do
47 | so, subject to the following conditions:
48 |
49 | * Redistributions of source code must retain the above copyright notice,
50 | this list of conditions and the following disclaimers.
51 |
52 | * Redistributions in binary form must reproduce the above copyright notice,
53 | this list of conditions and the following disclaimers in the
54 | documentation and/or other materials provided with the distribution.
55 |
56 | * Neither the names of the LLVM Team, University of Illinois at
57 | Urbana-Champaign, nor the names of its contributors may be used to
58 | endorse or promote products derived from this Software without specific
59 | prior written permission.
60 |
61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
63 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
64 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
67 | SOFTWARE.
--------------------------------------------------------------------------------
/macbuild.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
clear
rm NOTPSXSERIAL.EXE
rm NoPS.EXE
rm nops.exe
# Old way, no elfsharp
# csc TransferLogic.cs NOTPSXSERIAL.cs GDB.cs Utils.cs -out:nops.exe
msbuild nops_sln.sln
--------------------------------------------------------------------------------
/macrun.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
mono nops.exe /exe psx_helloworld.exe /dev/tty.SLAB_USBtoUART
--------------------------------------------------------------------------------
/nops:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | mono nops.exe $@
3 |
4 |
--------------------------------------------------------------------------------
/nops.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/nops.exe
--------------------------------------------------------------------------------
/nops.exe.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/nops.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/nops.ico
--------------------------------------------------------------------------------
/nops_icon_PSD.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/nops_icon_PSD.psd
--------------------------------------------------------------------------------
/nops_proj.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 | x86
5 | {E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}
6 | Exe
7 | false
8 | nops
9 | v4.7.2
10 |
11 |
12 | 512
13 |
14 |
15 | true
16 | full
17 | false
18 | .\
19 | TRACE;DEBUG;USE_ELFSHARP
20 | prompt
21 | 4
22 | x86
23 | false
24 |
25 |
26 | pdbonly
27 | true
28 | .\
29 | TRACE;USE_ELFSHARP
30 | prompt
31 | 4
32 | x86
33 | false
34 |
35 |
36 | nops
37 |
38 |
39 | Program
40 |
41 |
42 | nops.ico
43 |
44 |
45 |
46 | False
47 | .\ElfSharp.dll
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/nops_proj.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /fast /gdb 127.0.0.1:3333 COM19 /m
5 |
6 |
--------------------------------------------------------------------------------
/nops_sln.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30011.22
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "nops_proj", "nops_proj.csproj", "{E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7756F883-6592-4C07-8784-388418142325}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|x86 = Debug|x86
16 | Release|x86 = Release|x86
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}.Debug|x86.ActiveCfg = Debug|x86
20 | {E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}.Debug|x86.Build.0 = Debug|x86
21 | {E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}.Release|x86.ActiveCfg = Release|x86
22 | {E2AD2BDD-29F6-4990-854F-E5AA7E8DAC10}.Release|x86.Build.0 = Release|x86
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {A20D7F1F-310B-41BF-A0A6-0D1BE223FFA8}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/psx_helloworld.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/psx_helloworld.exe
--------------------------------------------------------------------------------
/social_card_PNG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/social_card_PNG.png
--------------------------------------------------------------------------------
/social_card_PSD.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonathanDotCel/NOTPSXSerial/2ae6c5e074f7bf8a8f690ad0a01e8e7efd993526/social_card_PSD.psd
--------------------------------------------------------------------------------