Compare commits
10 Commits
ee9a6874d9
...
07a0b90d36
Author | SHA1 | Date |
---|---|---|
Simon Gellis | 07a0b90d36 | |
Simon Gellis | 62722d28f7 | |
Simon Gellis | 062ea1c19a | |
Simon Gellis | 1b0d2d61b3 | |
Simon Gellis | bb680f9be4 | |
Simon Gellis | 26ecf29a3f | |
Simon Gellis | 884b52b397 | |
Simon Gellis | 57403690ff | |
Simon Gellis | 431377f92e | |
Simon Gellis | 8d6f380f6b |
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "pvbemu"]
|
||||||
|
path = pvbemu
|
||||||
|
url = https://git.virtual-boy.com/PVB/pvbemu.git
|
|
@ -0,0 +1,5 @@
|
||||||
|
# V810 Standalone GDB Server
|
||||||
|
|
||||||
|
This is a GDB/LLDB-compatible remote serial protocol server wrapping the [shrooms-vb](https://git.virtual-boy.com/PVB/pvbemu) emulation core. You can use it with [a v810-compatible version of lldb](https://github.com/SupernaviX/v810-llvm/releases) to debug your Virtual Boy games in your favorite IDE.
|
||||||
|
|
||||||
|
This program has no input or output, so it's more of a POC than a usable tool. Future plans are to add the server to a full emulator.
|
12
cmdbuf.c
12
cmdbuf.c
|
@ -2,7 +2,7 @@
|
||||||
#include <hex.h>
|
#include <hex.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
bool cmd_match_str(CommandBuf *cmd, const char *str) {
|
bool cmdMatchStr(CommandBuf *cmd, const char *str) {
|
||||||
size_t len = strlen(str);
|
size_t len = strlen(str);
|
||||||
if (cmd->len < len) return false;
|
if (cmd->len < len) return false;
|
||||||
if (!strncmp(cmd->buf, str, len)) {
|
if (!strncmp(cmd->buf, str, len)) {
|
||||||
|
@ -13,7 +13,7 @@ bool cmd_match_str(CommandBuf *cmd, const char *str) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_match_hex_number(CommandBuf *cmd, uint32_t *value) {
|
bool cmdMatchHexNumber(CommandBuf *cmd, uint32_t *value) {
|
||||||
size_t read = 0;
|
size_t read = 0;
|
||||||
size_t max_len = cmd->len;
|
size_t max_len = cmd->len;
|
||||||
if (max_len > 8) max_len = 8;
|
if (max_len > 8) max_len = 8;
|
||||||
|
@ -21,7 +21,7 @@ bool cmd_match_hex_number(CommandBuf *cmd, uint32_t *value) {
|
||||||
*value = 0;
|
*value = 0;
|
||||||
for (; read < max_len; ++read) {
|
for (; read < max_len; ++read) {
|
||||||
char digit;
|
char digit;
|
||||||
if (!parse_hex_digit(cmd->buf[read], &digit)) break;
|
if (!parseHexDigit(cmd->buf[read], &digit)) break;
|
||||||
*value = (*value << 4) | digit;
|
*value = (*value << 4) | digit;
|
||||||
}
|
}
|
||||||
if (!read) return false;
|
if (!read) return false;
|
||||||
|
@ -31,13 +31,13 @@ bool cmd_match_hex_number(CommandBuf *cmd, uint32_t *value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_match_hex_bytes(CommandBuf *cmd, const uint32_t count, uint8_t *value) {
|
bool cmdMatchHexBytes(CommandBuf *cmd, const uint32_t count, uint8_t *value) {
|
||||||
size_t i;
|
size_t i;
|
||||||
if (cmd->len < (count * 2)) return false;
|
if (cmd->len < (count * 2)) return false;
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 0; i < count; ++i) {
|
||||||
char hi, lo;
|
char hi, lo;
|
||||||
if (!parse_hex_digit(cmd->buf[i * 2], &hi)) return false;
|
if (!parseHexDigit(cmd->buf[i * 2], &hi)) return false;
|
||||||
if (!parse_hex_digit(cmd->buf[(i * 2) + 1], &lo)) return false;
|
if (!parseHexDigit(cmd->buf[(i * 2) + 1], &lo)) return false;
|
||||||
value[i] = (hi << 4) | lo;
|
value[i] = (hi << 4) | lo;
|
||||||
}
|
}
|
||||||
cmd->buf += (count * 2);
|
cmd->buf += (count * 2);
|
||||||
|
|
2
hex.c
2
hex.c
|
@ -1,6 +1,6 @@
|
||||||
#include <hex.h>
|
#include <hex.h>
|
||||||
|
|
||||||
bool parse_hex_digit(char digit, char *out) {
|
bool parseHexDigit(char digit, char *out) {
|
||||||
if (digit >= '0' && digit <= '9') {
|
if (digit >= '0' && digit <= '9') {
|
||||||
*out = digit - '0';
|
*out = digit - '0';
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -11,10 +11,10 @@ typedef struct CommandBuf {
|
||||||
} CommandBuf;
|
} CommandBuf;
|
||||||
|
|
||||||
/* Try to consume a string literal. */
|
/* Try to consume a string literal. */
|
||||||
bool cmd_match_str(CommandBuf *cmd, const char *str);
|
bool cmdMatchStr(CommandBuf *cmd, const char *str);
|
||||||
/* Try to consume a base-16 number, and return the value. */
|
/* Try to consume a base-16 number, and return the value. */
|
||||||
bool cmd_match_hex_number(CommandBuf *cmd, uint32_t *value);
|
bool cmdMatchHexNumber(CommandBuf *cmd, uint32_t *value);
|
||||||
/* Try to consume a hex-encoded list of bytes, and return the value. */
|
/* Try to consume a hex-encoded list of bytes, and return the value. */
|
||||||
bool cmd_match_hex_bytes(CommandBuf *cmd, const uint32_t count, uint8_t *value);
|
bool cmdMatchHexBytes(CommandBuf *cmd, const uint32_t count, uint8_t *value);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
bool parse_hex_digit(char digit, char *out);
|
bool parseHexDigit(char digit, char *out);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -35,9 +35,9 @@ typedef struct RdbRequest {
|
||||||
char chk;
|
char chk;
|
||||||
} RdbRequest;
|
} RdbRequest;
|
||||||
|
|
||||||
void rdb_request_init(RdbRequest *req, int connfd, char *buf, size_t buflen);
|
void rdbRequestInit(RdbRequest *req, int connfd, char *buf, size_t buflen);
|
||||||
void rdb_request_reset(RdbRequest *req);
|
void rdbRequestReset(RdbRequest *req);
|
||||||
void rdb_request_set_blocking(RdbRequest *req, bool blocking);
|
void rdbRequestSetBlocking(RdbRequest *req, bool blocking);
|
||||||
rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd);
|
rdb_read_result_t rdbRequestRead(RdbRequest *req, CommandBuf *cmd);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -15,12 +15,12 @@ typedef struct RdbResponse {
|
||||||
bool should_ack;
|
bool should_ack;
|
||||||
} RdbResponse;
|
} RdbResponse;
|
||||||
|
|
||||||
void rdb_response_init(RdbResponse *self, int connfd, char *buf, size_t buflen);
|
void rdbResponseInit(RdbResponse *self, int connfd, char *buf, size_t buflen);
|
||||||
void rdb_response_begin_packet(RdbResponse *self);
|
void rdbResponseBeginPacket(RdbResponse *self);
|
||||||
bool rdb_response_write_str(RdbResponse *self, const char *str);
|
bool rdbResponseWriteStr(RdbResponse *self, const char *str);
|
||||||
bool rdb_response_write_str_hex(RdbResponse *self, const char *str);
|
bool rdbResponseWriteStrHex(RdbResponse *self, const char *str);
|
||||||
bool rdb_response_write_i8_hex(RdbResponse *self, uint8_t value);
|
bool rdbResponseWriteI8Hex(RdbResponse *self, uint8_t value);
|
||||||
bool rdb_response_write_i32_hex(RdbResponse *self, uint32_t value);
|
bool rdbResponseWriteI32Hex(RdbResponse *self, uint32_t value);
|
||||||
int rdb_response_send_packet(RdbResponse *self);
|
int rdbResponseSendPacket(RdbResponse *self);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -6,6 +6,37 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <vb.h>
|
#include <vb.h>
|
||||||
|
|
||||||
int handle_command(RdbResponse *res, CommandBuf *cmd, VB *sim, bool *running);
|
typedef enum rdb_server_state_t {
|
||||||
|
state_stopped,
|
||||||
|
state_running,
|
||||||
|
state_stepping
|
||||||
|
} rdb_server_state_t;
|
||||||
|
|
||||||
|
typedef enum rdb_server_stop_reason_t {
|
||||||
|
/* not even stopped */
|
||||||
|
stop_reason_none,
|
||||||
|
/* we are stepping */
|
||||||
|
stop_reason_trace,
|
||||||
|
/* we hit an actual breakpoint */
|
||||||
|
stop_reason_breakpoint,
|
||||||
|
/* the user hit pause */
|
||||||
|
stop_reason_trap,
|
||||||
|
/* some opcode is not implemented */
|
||||||
|
stop_reason_not_implemented
|
||||||
|
} rdb_server_stop_reason_t;
|
||||||
|
|
||||||
|
#define RDB_SERVER_MAX_BREAKPOINTS 16
|
||||||
|
typedef struct RdbServer {
|
||||||
|
VB *sim;
|
||||||
|
uint32_t brks[RDB_SERVER_MAX_BREAKPOINTS];
|
||||||
|
uint32_t brkslen;
|
||||||
|
rdb_server_state_t state;
|
||||||
|
rdb_server_stop_reason_t stopreason;
|
||||||
|
} RdbServer;
|
||||||
|
|
||||||
|
void rdbServerInit(RdbServer *srv, VB *sim);
|
||||||
|
bool rdbServerIsRunning(RdbServer *srv);
|
||||||
|
int rdbServerHandleCommand(RdbServer *srv, CommandBuf *cmd, RdbResponse *res);
|
||||||
|
int rdbServerSendStopPacket(RdbServer *srv, RdbResponse *res);
|
||||||
|
|
||||||
#endif
|
#endif
|
52
main.c
52
main.c
|
@ -1,52 +1,77 @@
|
||||||
#include <cmdbuf.h>
|
#include <cmdbuf.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
#include <request.h>
|
#include <request.h>
|
||||||
#include <response.h>
|
#include <response.h>
|
||||||
#include <server.h>
|
#include <server.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <netinet/in.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vb.h>
|
#include <vb.h>
|
||||||
|
|
||||||
#define BUFLEN 8096
|
#define BUFLEN 8096
|
||||||
|
#define MAX_STEP_CYCLES 20000
|
||||||
|
|
||||||
|
int sleepNanos(long int ns) {
|
||||||
|
struct timespec time;
|
||||||
|
time.tv_sec = ns / 1000000000;
|
||||||
|
time.tv_nsec = ns % 1000000000;
|
||||||
|
return nanosleep(&time, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int server(int connfd, VB *sim) {
|
int server(int connfd, VB *sim) {
|
||||||
RdbRequest req;
|
RdbRequest req;
|
||||||
RdbResponse res;
|
RdbResponse res;
|
||||||
|
RdbServer srv;
|
||||||
char reqbuf[BUFLEN];
|
char reqbuf[BUFLEN];
|
||||||
char resbuf[BUFLEN];
|
char resbuf[BUFLEN];
|
||||||
bool running;
|
|
||||||
rdb_read_result_t read_result;
|
rdb_read_result_t read_result;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
rdb_request_init(&req, connfd, reqbuf, BUFLEN);
|
rdbRequestInit(&req, connfd, reqbuf, BUFLEN);
|
||||||
rdb_response_init(&res, connfd, resbuf, BUFLEN);
|
rdbResponseInit(&res, connfd, resbuf, BUFLEN);
|
||||||
|
rdbServerInit(&srv, sim);
|
||||||
running = false;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
CommandBuf cmd;
|
CommandBuf cmd;
|
||||||
read_result = rdb_request_read(&req, &cmd);
|
int brk;
|
||||||
|
uint32_t cycles;
|
||||||
|
read_result = rdbRequestRead(&req, &cmd);
|
||||||
if (read_result == read_result_error) {
|
if (read_result == read_result_error) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (read_result == read_result_disconnected) {
|
} else if (read_result == read_result_disconnected) {
|
||||||
printf("client has disconnected\n");
|
printf("client has disconnected\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (read_result == read_result_pending) {
|
} else if (read_result == read_result_pending) {
|
||||||
if (running) {
|
if (rdbServerIsRunning(&srv)) {
|
||||||
printf("pretend the emulator is running now\n");
|
cycles = MAX_STEP_CYCLES;
|
||||||
sleep(1);
|
brk = vbEmulate(sim, &cycles);
|
||||||
|
if (brk) {
|
||||||
|
/* We stopped for some reason */
|
||||||
|
if (brk == -1) {
|
||||||
|
/* the reason was "opcode not implemented" */
|
||||||
|
srv.state = state_stopped;
|
||||||
|
srv.stopreason = stop_reason_not_implemented;
|
||||||
|
}
|
||||||
|
result = rdbServerSendStopPacket(&srv, &res);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sleepNanos((MAX_STEP_CYCLES - cycles) * 50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
printf("received command \"%.*s\"\n", (int) cmd.len, cmd.buf);
|
printf("received command \"%.*s\"\n", (int) cmd.len, cmd.buf);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
result = handle_command(&res, &cmd, sim, &running);
|
result = rdbServerHandleCommand(&srv, &cmd, &res);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
rdb_request_set_blocking(&req, !running);
|
rdbRequestSetBlocking(&req, !rdbServerIsRunning(&srv));
|
||||||
rdb_request_reset(&req);
|
rdbRequestReset(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -115,7 +140,6 @@ int main(int argc, char** argv) {
|
||||||
if (readROM(sim, argv[1])) {
|
if (readROM(sim, argv[1])) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
vbSetProgramCounter(sim, 0x07000000);
|
|
||||||
|
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
char *end;
|
char *end;
|
||||||
|
|
6
makefile
6
makefile
|
@ -1,9 +1,9 @@
|
||||||
build:
|
build:
|
||||||
@mkdir -p build
|
@mkdir -p build
|
||||||
@gcc main.c cmdbuf.c hex.c request.c response.c server.c ../vbtest/vb.c \
|
@gcc main.c cmdbuf.c hex.c request.c response.c server.c pvbemu/core/vb.c \
|
||||||
-I include -I ../vbtest \
|
-I include -I pvbemu/core \
|
||||||
-Werror -std=c90 -Wall -Wextra -Wpedantic \
|
-Werror -std=c90 -Wall -Wextra -Wpedantic \
|
||||||
-Wno-unused-parameter -Wno-unused-function \
|
-D _POSIX_C_SOURCE=199309L \
|
||||||
-o ./build/rdb
|
-o ./build/rdb
|
||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5f563e6cac80d799f6c0251f9f601321e3603ea5
|
25
request.c
25
request.c
|
@ -5,25 +5,26 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void rdb_request_init(RdbRequest *req, int connfd, char *buf, size_t buflen) {
|
void rdbRequestInit(RdbRequest *req, int connfd, char *buf, size_t buflen) {
|
||||||
req->connfd = connfd;
|
req->connfd = connfd;
|
||||||
req->outbuf = buf;
|
req->outbuf = buf;
|
||||||
|
req->outbuflen = buflen;
|
||||||
req->blocking = true;
|
req->blocking = true;
|
||||||
rdb_request_reset(req);
|
rdbRequestReset(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdb_request_reset(RdbRequest *req) {
|
void rdbRequestReset(RdbRequest *req) {
|
||||||
req->state = read_state_header;
|
req->state = read_state_header;
|
||||||
req->inbuf.len = 0;
|
req->inbuf.len = 0;
|
||||||
req->inbuf.index = 0;
|
req->inbuf.index = 0;
|
||||||
req->chk = 0;
|
req->chk = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdb_request_set_blocking(RdbRequest *req, bool blocking) {
|
void rdbRequestSetBlocking(RdbRequest *req, bool blocking) {
|
||||||
req->blocking = blocking;
|
req->blocking = blocking;
|
||||||
}
|
}
|
||||||
|
|
||||||
static rdb_read_result_t read_char(RdbRequest *req, char *in) {
|
static rdb_read_result_t readChar(RdbRequest *req, char *in) {
|
||||||
if (req->inbuf.index >= req->inbuf.len) {
|
if (req->inbuf.index >= req->inbuf.len) {
|
||||||
int flags = req->blocking ? 0 : MSG_DONTWAIT;
|
int flags = req->blocking ? 0 : MSG_DONTWAIT;
|
||||||
ssize_t inlen = recv(req->connfd, req->inbuf.buf, INBUF_LEN, flags);
|
ssize_t inlen = recv(req->connfd, req->inbuf.buf, INBUF_LEN, flags);
|
||||||
|
@ -45,7 +46,7 @@ static rdb_read_result_t read_char(RdbRequest *req, char *in) {
|
||||||
return read_result_success;
|
return read_result_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd) {
|
rdb_read_result_t rdbRequestRead(RdbRequest *req, CommandBuf *cmd) {
|
||||||
rdb_read_result_t res;
|
rdb_read_result_t res;
|
||||||
char in, hi, lo;
|
char in, hi, lo;
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd) {
|
||||||
|
|
||||||
/* read any acknowledgements and continue */
|
/* read any acknowledgements and continue */
|
||||||
do {
|
do {
|
||||||
res = read_char(req, &in);
|
res = readChar(req, &in);
|
||||||
if (res != read_result_success) return res;
|
if (res != read_result_success) return res;
|
||||||
} while (in == '+');
|
} while (in == '+');
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd) {
|
||||||
case read_state_body:
|
case read_state_body:
|
||||||
case read_state_body_escape:
|
case read_state_body_escape:
|
||||||
while (1) {
|
while (1) {
|
||||||
res = read_char(req, &in);
|
res = readChar(req, &in);
|
||||||
if (res != read_result_success) return res;
|
if (res != read_result_success) return res;
|
||||||
|
|
||||||
if (req->state == read_state_body && in == '#') {
|
if (req->state == read_state_body && in == '#') {
|
||||||
|
@ -115,11 +116,11 @@ rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd) {
|
||||||
req->state = read_state_checksum_1;
|
req->state = read_state_checksum_1;
|
||||||
__attribute__ ((fallthrough));
|
__attribute__ ((fallthrough));
|
||||||
case read_state_checksum_1:
|
case read_state_checksum_1:
|
||||||
res = read_char(req, &in);
|
res = readChar(req, &in);
|
||||||
if (res != read_result_success) return res;
|
if (res != read_result_success) return res;
|
||||||
|
|
||||||
/* check the high digit of the checksum */
|
/* check the high digit of the checksum */
|
||||||
if (!parse_hex_digit(in, &hi)) {
|
if (!parseHexDigit(in, &hi)) {
|
||||||
fprintf(stderr, "invalid checksum1\n");
|
fprintf(stderr, "invalid checksum1\n");
|
||||||
return read_result_error;
|
return read_result_error;
|
||||||
}
|
}
|
||||||
|
@ -130,11 +131,11 @@ rdb_read_result_t rdb_request_read(RdbRequest *req, CommandBuf *cmd) {
|
||||||
req->state = read_state_checksum_2;
|
req->state = read_state_checksum_2;
|
||||||
__attribute__ ((fallthrough));
|
__attribute__ ((fallthrough));
|
||||||
case read_state_checksum_2:
|
case read_state_checksum_2:
|
||||||
res = read_char(req, &in);
|
res = readChar(req, &in);
|
||||||
if (res != read_result_success) return res;
|
if (res != read_result_success) return res;
|
||||||
|
|
||||||
/* check the high digit of the checksum */
|
/* check the high digit of the checksum */
|
||||||
if (!parse_hex_digit(in, &lo)) {
|
if (!parseHexDigit(in, &lo)) {
|
||||||
fprintf(stderr, "invalid checksum3 %c\n", in);
|
fprintf(stderr, "invalid checksum3 %c\n", in);
|
||||||
return read_result_error;
|
return read_result_error;
|
||||||
}
|
}
|
||||||
|
|
48
response.c
48
response.c
|
@ -4,7 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static bool write_char(RdbResponse *res, char out) {
|
static bool writeChar(RdbResponse *res, char out) {
|
||||||
if (res->len >= res->buflen) {
|
if (res->len >= res->buflen) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ static bool write_char(RdbResponse *res, char out) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool char_to_hex_digit(char in, char *out) {
|
bool charToHexDigit(char in, char *out) {
|
||||||
if (in & 0xf0) {
|
if (in & 0xf0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,12 @@ bool char_to_hex_digit(char in, char *out) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool char_to_hex_digits(char in, char *hi, char *lo) {
|
bool charToHexDigits(char in, char *hi, char *lo) {
|
||||||
return char_to_hex_digit((in & 0xf0) >> 4, hi)
|
return charToHexDigit((in & 0xf0) >> 4, hi)
|
||||||
&& char_to_hex_digit(in & 0x0f, lo);
|
&& charToHexDigit(in & 0x0f, lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdb_response_init(RdbResponse *res, int connfd, char *buf, size_t buflen) {
|
void rdbResponseInit(RdbResponse *res, int connfd, char *buf, size_t buflen) {
|
||||||
res->connfd = connfd;
|
res->connfd = connfd;
|
||||||
res->buf = buf;
|
res->buf = buf;
|
||||||
res->buflen = buflen;
|
res->buflen = buflen;
|
||||||
|
@ -48,7 +48,7 @@ void rdb_response_init(RdbResponse *res, int connfd, char *buf, size_t buflen) {
|
||||||
res->should_ack = true;
|
res->should_ack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdb_response_begin_packet(RdbResponse *res) {
|
void rdbResponseBeginPacket(RdbResponse *res) {
|
||||||
res->len = 0;
|
res->len = 0;
|
||||||
res->chk = 0;
|
res->chk = 0;
|
||||||
if (res->should_ack) {
|
if (res->should_ack) {
|
||||||
|
@ -57,44 +57,44 @@ void rdb_response_begin_packet(RdbResponse *res) {
|
||||||
res->buf[res->len++] = '$';
|
res->buf[res->len++] = '$';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rdb_response_write_str(RdbResponse *res, const char *str) {
|
bool rdbResponseWriteStr(RdbResponse *res, const char *str) {
|
||||||
size_t i, len = strlen(str);
|
size_t i, len = strlen(str);
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
if (!write_char(res, str[i])) {
|
if (!writeChar(res, str[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rdb_response_write_str_hex(RdbResponse *res, const char *str) {
|
bool rdbResponseWriteStrHex(RdbResponse *res, const char *str) {
|
||||||
size_t i, len = strlen(str);
|
size_t i, len = strlen(str);
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
char hi, lo;
|
char hi, lo;
|
||||||
if (!char_to_hex_digits(str[i], &hi, &lo)
|
if (!charToHexDigits(str[i], &hi, &lo)
|
||||||
|| !write_char(res, hi)
|
|| !writeChar(res, hi)
|
||||||
|| !write_char(res, lo)) {
|
|| !writeChar(res, lo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rdb_response_write_i8_hex(RdbResponse *res, uint8_t value) {
|
bool rdbResponseWriteI8Hex(RdbResponse *res, uint8_t value) {
|
||||||
char hi, lo;
|
char hi, lo;
|
||||||
return char_to_hex_digits(value, &hi, &lo)
|
return charToHexDigits(value, &hi, &lo)
|
||||||
&& write_char(res, hi)
|
&& writeChar(res, hi)
|
||||||
&& write_char(res, lo);
|
&& writeChar(res, lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rdb_response_write_i32_hex(RdbResponse *res, uint32_t value) {
|
bool rdbResponseWriteI32Hex(RdbResponse *res, uint32_t value) {
|
||||||
return rdb_response_write_i8_hex(res, (uint8_t) value)
|
return rdbResponseWriteI8Hex(res, (uint8_t) value)
|
||||||
&& rdb_response_write_i8_hex(res, (uint8_t) (value >> 8))
|
&& rdbResponseWriteI8Hex(res, (uint8_t) (value >> 8))
|
||||||
&& rdb_response_write_i8_hex(res, (uint8_t) (value >> 16))
|
&& rdbResponseWriteI8Hex(res, (uint8_t) (value >> 16))
|
||||||
&& rdb_response_write_i8_hex(res, (uint8_t) (value >> 24));
|
&& rdbResponseWriteI8Hex(res, (uint8_t) (value >> 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
int rdb_response_send_packet(RdbResponse *res) {
|
int rdbResponseSendPacket(RdbResponse *res) {
|
||||||
char hi, lo;
|
char hi, lo;
|
||||||
ssize_t rwrite;
|
ssize_t rwrite;
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ int rdb_response_send_packet(RdbResponse *res) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
res->buf[res->len++] = '#';
|
res->buf[res->len++] = '#';
|
||||||
if (!char_to_hex_digits(res->chk, &hi, &lo)) {
|
if (!charToHexDigits(res->chk, &hi, &lo)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
res->buf[res->len++] = hi;
|
res->buf[res->len++] = hi;
|
||||||
|
|
304
server.c
304
server.c
|
@ -69,103 +69,175 @@ const uint32_t SYSTEM_REGISTERS[] = {
|
||||||
|
|
||||||
const uint32_t PC_INDEX = 32 + 13;
|
const uint32_t PC_INDEX = 32 + 13;
|
||||||
|
|
||||||
int handle_command(RdbResponse *res, CommandBuf *cmd, VB *sim, bool *running) {
|
static int onExecute(VB *sim, uint32_t address, const uint16_t *code, int length) {
|
||||||
rdb_response_begin_packet(res);
|
uint32_t i;
|
||||||
|
RdbServer *srv = (RdbServer *)vbGetUserData(sim);
|
||||||
|
|
||||||
if (cmd_match_str(cmd, "QStartNoAckMode")) {
|
(void)sim;
|
||||||
|
(void)code;
|
||||||
|
(void)length;
|
||||||
|
|
||||||
|
/* if we're stopped, just stop */
|
||||||
|
if (srv->state == state_stopped) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we're stepping, we'll run this one instruction but no others */
|
||||||
|
if (srv->state == state_stepping) {
|
||||||
|
srv->state = state_stopped;
|
||||||
|
srv->stopreason = stop_reason_trace;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < srv->brkslen; ++i) {
|
||||||
|
if (srv->brks[i] == address) {
|
||||||
|
srv->state = state_stopped;
|
||||||
|
srv->stopreason = stop_reason_breakpoint;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool addBreakpoint(RdbServer *srv, uint32_t address) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < srv->brkslen; ++i) {
|
||||||
|
if (srv->brks[i] == address) {
|
||||||
|
/* This breakpoint is already set */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == RDB_SERVER_MAX_BREAKPOINTS) {
|
||||||
|
/* We've added too many breakpoints */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
srv->brks[i] = address;
|
||||||
|
++srv->brkslen;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeBreakpoint(RdbServer *srv, uint32_t address) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < srv->brkslen; ++i) {
|
||||||
|
if (srv->brks[i] == address) {
|
||||||
|
srv->brks[i] = srv->brks[srv->brkslen - 1];
|
||||||
|
--srv->brkslen;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rdbServerInit(RdbServer *srv, VB *sim) {
|
||||||
|
srv->sim = sim;
|
||||||
|
srv->brkslen = 0;
|
||||||
|
srv->state = state_stopped;
|
||||||
|
srv->stopreason = stop_reason_none;
|
||||||
|
vbSetUserData(sim, srv);
|
||||||
|
vbSetExecuteCallback(sim, onExecute);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rdbServerHandleCommand(RdbServer *srv, CommandBuf *cmd, RdbResponse *res) {
|
||||||
|
rdbResponseBeginPacket(res);
|
||||||
|
|
||||||
|
if (cmdMatchStr(cmd, "QStartNoAckMode")) {
|
||||||
/* The debugger is asking us to no longer ACK messages. */
|
/* The debugger is asking us to no longer ACK messages. */
|
||||||
/* Note that we ack THIS response, because we already called rdb_response_begin_packet. */
|
/* Note that we ack THIS response, because we already called rdbResponseBeginPacket. */
|
||||||
res->should_ack = false;
|
res->should_ack = false;
|
||||||
rdb_response_write_str(res, "OK");
|
rdbResponseWriteStr(res, "OK");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qSupported")) {
|
if (cmdMatchStr(cmd, "qSupported")) {
|
||||||
/* The debugger is asking for a list of features we support. */
|
/* The debugger is asking for a list of features we support. */
|
||||||
rdb_response_write_str(res, "no-resumed+;multiprocess;vContSupported;QNonStop+");
|
rdbResponseWriteStr(res, "no-resumed+;multiprocess;vContSupported");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "QThreadSuffixSupported")) {
|
if (cmdMatchStr(cmd, "QThreadSuffixSupported")) {
|
||||||
/* The debugger is asking us to include the current thread as a suffix to some responses. */
|
/* The debugger is asking us to include the current thread as a suffix to some responses. */
|
||||||
rdb_response_write_str(res, "OK");
|
rdbResponseWriteStr(res, "OK");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "QListThreadsInStopReply")) {
|
if (cmdMatchStr(cmd, "QListThreadsInStopReply")) {
|
||||||
/* The debugger is asking us to list all threads whenever we stop running. */
|
/* The debugger is asking us to list all threads whenever we stop running. */
|
||||||
rdb_response_write_str(res, "OK");
|
rdbResponseWriteStr(res, "OK");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qHostInfo")) {
|
if (cmdMatchStr(cmd, "qHostInfo")) {
|
||||||
/* The debugger is asking us to describe the "host machine" getting debugged. */
|
/* The debugger is asking us to describe the "host machine" getting debugged. */
|
||||||
rdb_response_write_str(res, "triple:");
|
rdbResponseWriteStr(res, "triple:");
|
||||||
rdb_response_write_str_hex(res, "v810-unknown-vb");
|
rdbResponseWriteStrHex(res, "v810-unknown-vb");
|
||||||
rdb_response_write_str(res, ";endian:little;ptrsize:4;");
|
rdbResponseWriteStr(res, ";endian:little;ptrsize:4;");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qProcessInfo")) {
|
if (cmdMatchStr(cmd, "qProcessInfo")) {
|
||||||
/* The debugger is asking us to describe the "process" getting debugged. */
|
/* The debugger is asking us to describe the "process" getting debugged. */
|
||||||
/* We make up a process with id 1. */
|
/* We make up a process with id 1. */
|
||||||
rdb_response_write_str(res, "pid:1;triple:");
|
rdbResponseWriteStr(res, "pid:1;triple:");
|
||||||
rdb_response_write_str_hex(res, "v810-unknown-vb");
|
rdbResponseWriteStrHex(res, "v810-unknown-vb");
|
||||||
rdb_response_write_str(res, "endian:little;ptrsize:4;");
|
rdbResponseWriteStr(res, "endian:little;ptrsize:4;");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qRegisterInfo")) {
|
if (cmdMatchStr(cmd, "qRegisterInfo")) {
|
||||||
uint32_t reg_no;
|
uint32_t reg_no;
|
||||||
|
|
||||||
/* The debugger is asking for information about a specific register. */
|
/* The debugger is asking for information about a specific register. */
|
||||||
if (!cmd_match_hex_number(cmd, ®_no)) return 1;
|
if (!cmdMatchHexNumber(cmd, ®_no)) return 1;
|
||||||
if (reg_no <= PC_INDEX) {
|
if (reg_no <= PC_INDEX) {
|
||||||
rdb_response_write_str(res, REGISTERS[reg_no]);
|
rdbResponseWriteStr(res, REGISTERS[reg_no]);
|
||||||
}
|
}
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qfThreadInfo")) {
|
if (cmdMatchStr(cmd, "qfThreadInfo")) {
|
||||||
/* The debugger is asking us to list all threads. Return a list with "thread 1". */
|
/* The debugger is asking us to list all threads. Return a list with "thread 1". */
|
||||||
rdb_response_write_str(res, "mp1.t1");
|
rdbResponseWriteStr(res, "mp1.t1");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qsThreadInfo")) {
|
if (cmdMatchStr(cmd, "qsThreadInfo")) {
|
||||||
/* The debugger is asking us to list all threads. */
|
/* The debugger is asking us to list all threads. */
|
||||||
rdb_response_write_str(res, "l");
|
rdbResponseWriteStr(res, "l");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "vCont?")) {
|
if (cmdMatchStr(cmd, "vCont?")) {
|
||||||
/* The debugger is asking which vCont commands we support. */
|
/* The debugger is asking which vCont commands we support. */
|
||||||
rdb_response_write_str(res, "c;C;s;S");
|
rdbResponseWriteStr(res, "c;C;s;S");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "qC")) {
|
if (cmdMatchStr(cmd, "vCont;s:1")) {
|
||||||
|
/* The debugger wants us to step */
|
||||||
|
srv->state = state_stepping;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (cmdMatchStr(cmd, "qC")) {
|
||||||
/* The debugger is asking for the current thread id. Return "thread 1". */
|
/* The debugger is asking for the current thread id. Return "thread 1". */
|
||||||
rdb_response_write_str(res, "QCp1.t1");
|
rdbResponseWriteStr(res, "QCp1.t1");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "p")) {
|
if (cmdMatchStr(cmd, "p")) {
|
||||||
uint32_t reg_no, reg_value;
|
uint32_t reg_no, reg_value;
|
||||||
|
|
||||||
/* read a register. */
|
/* read a register. */
|
||||||
if (!cmd_match_hex_number(cmd, ®_no)) return 1;
|
if (!cmdMatchHexNumber(cmd, ®_no)) return 1;
|
||||||
if (reg_no > PC_INDEX) {
|
if (reg_no > PC_INDEX) {
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg_no == PC_INDEX) {
|
if (reg_no == PC_INDEX) {
|
||||||
reg_value = vbGetProgramCounter(sim);
|
reg_value = vbGetProgramCounter(srv->sim);
|
||||||
} else if (reg_no > 31) {
|
} else if (reg_no > 31) {
|
||||||
reg_value = vbGetSystemRegister(sim, SYSTEM_REGISTERS[reg_no - 32]);
|
reg_value = vbGetSystemRegister(srv->sim, SYSTEM_REGISTERS[reg_no - 32]);
|
||||||
} else {
|
} else {
|
||||||
reg_value = vbGetProgramRegister(sim, reg_no);
|
reg_value = vbGetProgramRegister(srv->sim, reg_no);
|
||||||
}
|
}
|
||||||
rdb_response_write_i32_hex(res, reg_value);
|
rdbResponseWriteI32Hex(res, reg_value);
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "P")) {
|
if (cmdMatchStr(cmd, "P")) {
|
||||||
uint32_t reg_no, reg_value;
|
uint32_t reg_no, reg_value;
|
||||||
uint8_t reg_bytes[4];
|
uint8_t reg_bytes[4];
|
||||||
|
|
||||||
/* write a register. */
|
/* write a register. */
|
||||||
if (!cmd_match_hex_number(cmd, ®_no)) return -1;
|
if (!cmdMatchHexNumber(cmd, ®_no)) return -1;
|
||||||
if (!cmd_match_str(cmd, "=")) return -1;
|
if (!cmdMatchStr(cmd, "=")) return -1;
|
||||||
if (!cmd_match_hex_bytes(cmd, 4, reg_bytes)) return -1;
|
if (!cmdMatchHexBytes(cmd, 4, reg_bytes)) return -1;
|
||||||
|
|
||||||
reg_value = ((uint32_t) (reg_bytes[3]) << 24) |
|
reg_value = ((uint32_t) (reg_bytes[3]) << 24) |
|
||||||
((uint32_t) (reg_bytes[2]) << 16) |
|
((uint32_t) (reg_bytes[2]) << 16) |
|
||||||
|
@ -173,57 +245,127 @@ int handle_command(RdbResponse *res, CommandBuf *cmd, VB *sim, bool *running) {
|
||||||
((uint32_t) reg_bytes[0]);
|
((uint32_t) reg_bytes[0]);
|
||||||
|
|
||||||
if (reg_no > PC_INDEX) {
|
if (reg_no > PC_INDEX) {
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (reg_no == PC_INDEX) {
|
if (reg_no == PC_INDEX) {
|
||||||
vbSetProgramCounter(sim, reg_value);
|
vbSetProgramCounter(srv->sim, reg_value);
|
||||||
} else if (reg_no > 31) {
|
} else if (reg_no > 31) {
|
||||||
vbSetSystemRegister(sim, SYSTEM_REGISTERS[reg_no - 32], reg_value);
|
vbSetSystemRegister(srv->sim, SYSTEM_REGISTERS[reg_no - 32], reg_value);
|
||||||
} else {
|
} else {
|
||||||
vbSetProgramRegister(sim, reg_no, reg_value);
|
vbSetProgramRegister(srv->sim, reg_no, reg_value);
|
||||||
}
|
}
|
||||||
rdb_response_write_str(res, "OK");
|
rdbResponseWriteStr(res, "OK");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "Hc-1")) {
|
if (cmdMatchStr(cmd, "Hc-1")) {
|
||||||
/* Set the "current thread" for future commands to all threads (thread -1). */
|
/* Set the "current thread" for future commands to all threads (thread -1). */
|
||||||
rdb_response_write_str(res, "OK");
|
rdbResponseWriteStr(res, "OK");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "c")) {
|
if (cmdMatchStr(cmd, "c")) {
|
||||||
/* The debugger has told us to run until we are stopped. */
|
/* The debugger has told us to run until we are stopped. */
|
||||||
/* Don't send a response to this until we receive an ETX (when the debugger pauses us). */
|
/* Don't send a response to this until we receive an ETX (when the debugger pauses us). */
|
||||||
*running = true;
|
srv->state = state_running;
|
||||||
|
srv->stopreason = stop_reason_none;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "\x03")) {
|
if (cmdMatchStr(cmd, "\x03")) {
|
||||||
/* Received an ETX, indicating that the server wants to cancel the "c" command from before. */
|
/* Received an ETX, indicating that the server wants to cancel the "c" command from before. */
|
||||||
*running = false;
|
srv->state = state_stopped;
|
||||||
|
srv->stopreason = stop_reason_trap;
|
||||||
/* Send the response to the "c" command from before. */
|
/* Send the response to the "c" command from before. */
|
||||||
rdb_response_write_str(res, "T05thread:p1.t1;threads:p1.t1");
|
return rdbServerSendStopPacket(srv, res);
|
||||||
return rdb_response_send_packet(res);
|
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "?")) {
|
if (cmdMatchStr(cmd, "?")) {
|
||||||
/* The debugger has asked us why we stopped */
|
/* The debugger has asked us why we stopped */
|
||||||
rdb_response_write_str(res, "T");
|
return rdbServerSendStopPacket(srv, res);
|
||||||
rdb_response_write_str(res, *running ? "00" : "05");
|
|
||||||
rdb_response_write_str(res, "thread:p1.t1;threads:p1.t1;");
|
|
||||||
return rdb_response_send_packet(res);
|
|
||||||
}
|
}
|
||||||
if (cmd_match_str(cmd, "m")) {
|
if (cmdMatchStr(cmd, "m")) {
|
||||||
/* read memory */
|
/* read memory */
|
||||||
uint32_t i, address, len;
|
uint32_t i, address, len;
|
||||||
if (!cmd_match_hex_number(cmd, &address)) return -1;
|
if (!cmdMatchHexNumber(cmd, &address)) return -1;
|
||||||
if (!cmd_match_str(cmd, ",")) return -1;
|
if (!cmdMatchStr(cmd, ",")) {
|
||||||
if (!cmd_match_hex_number(cmd, &len)) return -1;
|
/* if the server asks for too much memory, just error */
|
||||||
|
return rdbResponseSendPacket(res);
|
||||||
|
};
|
||||||
|
if (!cmdMatchHexNumber(cmd, &len)) return -1;
|
||||||
|
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
uint8_t byte = vbRead(sim, address + i, VB_U8);
|
uint8_t byte = vbRead(srv->sim, address + i, VB_U8);
|
||||||
rdb_response_write_i8_hex(res, byte);
|
rdbResponseWriteI8Hex(res, byte);
|
||||||
}
|
}
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
|
}
|
||||||
|
if (cmdMatchStr(cmd, "M")) {
|
||||||
|
/* write memory */
|
||||||
|
uint32_t i, address, len;
|
||||||
|
if (!cmdMatchHexNumber(cmd, &address)) return -1;
|
||||||
|
if (!cmdMatchStr(cmd, ",")) return -1;
|
||||||
|
if (!cmdMatchHexNumber(cmd, &len)) return -1;
|
||||||
|
if (!cmdMatchStr(cmd, ":")) return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
uint8_t byte;
|
||||||
|
if (!cmdMatchHexBytes(cmd, 1, &byte)) return -1;
|
||||||
|
vbWrite(srv->sim, address + i, VB_U8, byte);
|
||||||
|
}
|
||||||
|
rdbResponseWriteStr(res, "OK");
|
||||||
|
return rdbResponseSendPacket(res);
|
||||||
|
}
|
||||||
|
if (cmdMatchStr(cmd, "Z0,")) {
|
||||||
|
/* set a breakpoint */
|
||||||
|
uint32_t address;
|
||||||
|
if (!cmdMatchHexNumber(cmd, &address)) return -1;
|
||||||
|
if (!cmdMatchStr(cmd, ",0")) return -1;
|
||||||
|
|
||||||
|
if (!addBreakpoint(srv, address)) return -1;
|
||||||
|
rdbResponseWriteStr(res, "OK");
|
||||||
|
return rdbResponseSendPacket(res);
|
||||||
|
}
|
||||||
|
if (cmdMatchStr(cmd, "z0,")) {
|
||||||
|
/* remove a breakpoint */
|
||||||
|
uint32_t address;
|
||||||
|
if (!cmdMatchHexNumber(cmd, &address)) return -1;
|
||||||
|
if (!cmdMatchStr(cmd, ",0")) return -1;
|
||||||
|
|
||||||
|
removeBreakpoint(srv, address);
|
||||||
|
rdbResponseWriteStr(res, "OK");
|
||||||
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Unrecognized command.\n");
|
fprintf(stderr, "Unrecognized command.\n");
|
||||||
return rdb_response_send_packet(res);
|
return rdbResponseSendPacket(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rdbServerIsRunning(RdbServer *srv) {
|
||||||
|
/* stepping counts */
|
||||||
|
return srv->state != state_stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rdbServerSendStopPacket(RdbServer *srv, RdbResponse *res) {
|
||||||
|
rdbResponseBeginPacket(res);
|
||||||
|
switch (srv->stopreason) {
|
||||||
|
case stop_reason_trace:
|
||||||
|
case stop_reason_none:
|
||||||
|
rdbResponseWriteStr(res, "T00");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rdbResponseWriteStr(res, "T05");
|
||||||
|
}
|
||||||
|
rdbResponseWriteStr(res, "thread:p1.t1;threads:p1.t1;");
|
||||||
|
switch (srv->stopreason) {
|
||||||
|
case stop_reason_trace:
|
||||||
|
rdbResponseWriteStr(res, "reason:trace;");
|
||||||
|
break;
|
||||||
|
case stop_reason_breakpoint:
|
||||||
|
rdbResponseWriteStr(res, "reason:breakpoint;");
|
||||||
|
break;
|
||||||
|
case stop_reason_trap:
|
||||||
|
rdbResponseWriteStr(res, "reason:trap;");
|
||||||
|
break;
|
||||||
|
case stop_reason_not_implemented:
|
||||||
|
rdbResponseWriteStr(res, "reason:exception;description:opcode not implemented;");
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return rdbResponseSendPacket(res);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue