Created
February 22, 2014 16:46
-
-
Save ismell/9157843 to your computer and use it in GitHub Desktop.
ScanaStudio 2 Maple Bus decoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint loopfunc: true, undef: true, unused: false, strict: false */ | |
/* global ch_sdcka, ch_sdckb, dec_item_add_post_text, dec_item_add_comment, hex_add_byte, dark_colors, pkt_start, pkt_end, pkt_add_item, trs_go_before, dec_item_add_data, sample_val, ui_clear, ui_add_ch_selector, dec_item_add_sample_point, DRAW_0, DRAW_1, clear_dec_items, dec_item_new, dec_item_add_pre_text, get_ui_vals, add_to_err_log, trs_get_first, trs_is_not_last, abort_requested, trs_get_next, debug, trs_go_after, sample_rate, get_ch_color */ | |
/* | |
************************************************************************************* | |
SCANASTUDIO 2 MAPLE BUS DECODER | |
The following commented block allows some related informations to be displayed online | |
<DESCRIPTION> | |
Maple Bus Protocol Decoder. | |
</DESCRIPTION> | |
<RELEASE_NOTES> | |
V1.0: Initial release | |
</RELEASE_NOTES> | |
<AUTHOR_URL> | |
mailto:[email protected] | |
</AUTHOR_URL> | |
<HELP_URL> | |
http://www.ikalogic.com/ikalogic-products/scanastudio-2/decoders-repository/spi-decoder-online/ | |
</HELP_URL> | |
************************************************************************************* | |
*/ | |
/* The decoder name as it will apear to the users of this script | |
*/ | |
function get_dec_name() | |
{ | |
return "Maple Bus"; | |
} | |
/* The decoder version | |
*/ | |
function get_dec_ver() | |
{ | |
return "1.0"; | |
} | |
/* Author | |
*/ | |
function get_dec_auth() | |
{ | |
return "ismell"; | |
} | |
/* Graphical user interface for this decoder | |
*/ | |
function gui() | |
{ | |
ui_clear(); // clean up the User interface before drawing a new one. | |
ui_add_ch_selector("ch_sdcka","SDCKA (Pin 1)","SDCKA"); | |
ui_add_ch_selector("ch_sdckb","SDCKB (Pin 2)","SDCKB"); | |
} | |
/* Constants | |
*/ | |
var IDLE = 0; | |
var START_FIND_START_FRAME = 1; | |
var COUNTING_START_FRAME = 2; | |
var START_FRAME_FOUND = 3; | |
var sdcka_t, sdckb_t; | |
var PKT_COLOR_DATA; | |
var PKT_COLOR_DATA_TITLE; | |
var PKT_COLOR_START_TITLE; | |
var PKT_COLOR_ADR_TITLE; | |
var PKT_COLOR_ACK_TITLE; | |
var PKT_COLOR_NACK_TITLE; | |
var PKT_COLOR_STOP_TITLE; | |
var PKT_COLOR_NOISE_TITLE; | |
/* This is the function that will be called from ScanaStudio | |
to update the decoded items | |
*/ | |
function decode() | |
{ | |
PKT_COLOR_DATA = get_ch_light_color(ch_sdcka); | |
PKT_COLOR_DATA_TITLE = dark_colors.gray; | |
PKT_COLOR_START_TITLE = dark_colors.orange; | |
PKT_COLOR_ADR_TITLE = dark_colors.yellow; | |
PKT_COLOR_ACK_TITLE = dark_colors.green; | |
PKT_COLOR_NACK_TITLE = dark_colors.red; | |
PKT_COLOR_STOP_TITLE = dark_colors.blue; | |
PKT_COLOR_NOISE_TITLE = dark_colors.black; | |
clear_dec_items(); // Clears all the the decoder items and its content | |
get_ui_vals(); // Update the content of user interface variables | |
if (!check_scanastudio_support()) { | |
add_to_err_log("Please update your ScanaStudio software to the latest version to use this decoder"); | |
return; | |
} | |
sdcka_t = trs_get_first(ch_sdcka); | |
sdckb_t = trs_get_first(ch_sdckb); | |
// Find all START and STOP conditions | |
while (trs_is_not_last(ch_sdcka) || trs_is_not_last(ch_sdckb)) { | |
if (abort_requested()) // Allow the user to abort this script | |
{ | |
return false; | |
} | |
var start_a = find_start_frame(); | |
var end_a; | |
var data; | |
if (start_a) { | |
//TODO: Can we make parse data find an end condition instead? | |
end_a = find_end_frame(); | |
if (end_a) { | |
data = parse_data(start_a, end_a); | |
if (data) { | |
display_data(data); | |
} else { | |
debug("NO DATA FOUND!"); | |
} | |
} | |
} | |
} | |
} | |
function display_data(data) { | |
debug("Starting packet with " + data.length + " bytes"); | |
pkt_start("DATA"); | |
var parse = data.length >= 5; | |
var command, lrc = 0; | |
data.forEach(function(sample, idx) { | |
debug("Adding data item: "+JSON.stringify(sample)); | |
var pre = [], post = [], desc, hex_str; | |
hex_str = "DATA"; | |
if (parse) { | |
var port, peripherals; | |
if (idx === 0) { | |
pre.push("Additional Words"); | |
pre.push("Addl Words"); | |
pre.push("Words"); | |
hex_str = "WORDS"; | |
desc = sample.value + " Additional Words in Frame"; | |
} else if (idx === 1) { | |
pre.push("Sender Address"); | |
pre.push("Sender Addr"); | |
pre.push("S Addr"); | |
hex_str = "SENDER"; | |
port = get_port_from_address(sample.value); | |
peripherals = get_peripherals_from_address(sample.value); | |
desc = "Port " + port + " : " + peripherals.join(","); | |
} else if (idx === 2) { | |
pre.push("Receiver Address"); | |
pre.push("Receiver Addr"); | |
pre.push("R Addr"); | |
hex_str = "RECEIVER"; | |
port = get_port_from_address(sample.value); | |
peripherals = get_peripherals_from_address(sample.value); | |
desc = "Port " + port + " : " + peripherals.join(","); | |
} else if (idx === 3) { | |
pre.push("Command"); | |
pre.push("Cmd"); | |
hex_str = "COMMAND"; | |
command = sample.value; | |
desc = get_command_name(command); | |
} else if (idx == data.length - 1) { | |
pre.push("LRC"); | |
hex_str = "LRC"; | |
if (lrc == sample.value) { | |
post.push("OK"); | |
desc = "Packet matches LRC"; | |
} else { | |
post.push("BAD"); | |
desc = "LRC Error. Packet LRC: " + lrc; | |
} | |
} | |
lrc ^= sample.value; | |
} | |
pkt_add_item(sample.start, sample.end, hex_str, int_to_str_hex(sample.value), PKT_COLOR_DATA_TITLE, PKT_COLOR_DATA, true); | |
hex_add_byte(ch_sdcka, sample.start, sample.end, sample.value); | |
draw_bits(ch_sdcka, sample); | |
pre.forEach(function(str) { | |
dec_item_add_pre_text(str); | |
}); | |
post.forEach(function(str) { | |
dec_item_add_post_text(str); | |
}); | |
if (desc) dec_item_add_comment(desc); | |
}); | |
debug("Ending packet"); | |
pkt_end(); | |
} | |
function get_command_name(command) { | |
if (command == 1) | |
return "Request device information"; | |
if (command == 2) | |
return "Request extended device information"; | |
if (command == 3) | |
return "Reset device"; | |
if (command == 4) | |
return "Shutdown device"; | |
if (command == 5) | |
return "Device information (response)"; | |
if (command == 6) | |
return "Extended device information (response)"; | |
if (command == 7) | |
return "Command acknowledge (response)"; | |
if (command == 8) | |
return "Data transfer (response)"; | |
if (command == 9) | |
return "Get condition"; | |
if (command == 10) | |
return "Get memory information"; | |
if (command == 11) | |
return "Block read"; | |
if (command == 12) | |
return "Block write"; | |
if (command == 14) | |
return "Set condition"; | |
if (command == -1) | |
return "No response"; | |
if (command == -2) | |
return "Function code unsupported (response)"; | |
if (command == -3) | |
return "Unknown command (response)"; | |
if (command == -4) | |
return "Command needs to be sent again (response)"; | |
if (command == -5) | |
return "File error (response)"; | |
return "Unknown"; | |
} | |
function get_peripherals_from_address(address) { | |
var peripherals = []; | |
var peripheral = address & 0x2F; | |
if (peripheral === 0) { | |
peripherals.push("Dreamcast"); | |
} else { | |
if (peripheral & 0x20) { | |
peripherals.push("Main"); | |
} | |
if (peripheral & 0x10) { | |
peripherals.push("Sub 5"); | |
} | |
if (peripheral & 0x08) { | |
peripherals.push("Sub 4"); | |
} | |
if (peripheral & 0x04) { | |
peripherals.push("Sub 3"); | |
} | |
if (peripheral & 0x02) { | |
peripherals.push("Sub 2"); | |
} | |
if (peripheral & 0x01) { | |
peripherals.push("Sub 1"); | |
} | |
} | |
return peripherals; | |
} | |
function get_port_from_address(address) { | |
var port; | |
address = address & 0xC0; | |
if (address == 0xC0) { | |
port = "D"; | |
} else if (address == 0x80) { | |
port = "C"; | |
} else if (address == 0x40) { | |
port = "B"; | |
} else { | |
port = "A"; | |
} | |
return port; | |
} | |
function draw_bits(channel, sample) { | |
dec_item_new(channel, sample.start, sample.end); | |
dec_item_add_data(sample.value); | |
sample.bits.forEach(function(bit) { | |
//if (bit.channel == channel) { | |
dec_item_add_sample_point(channel, bit.sample, bit.value ? DRAW_1 : DRAW_0); | |
//} | |
}); | |
} | |
function next_sdcka() { | |
sdcka_t = trs_get_next(ch_sdcka); | |
} | |
function next_sdckb() { | |
sdckb_t = trs_get_next(ch_sdckb); | |
} | |
function parse_data(start_a, end_a) { | |
var phase = 1, count = 0, bits = []; | |
var start, end, bit; | |
sdcka_t = trs_go_after(ch_sdcka, start_a.sample); | |
sdckb_t = trs_go_before(ch_sdckb, start_a.sample); | |
var packet = []; | |
while (trs_is_not_last(ch_sdcka) || trs_is_not_last(ch_sdckb)) { | |
if (abort_requested()) { | |
return false; | |
} | |
if (sdcka_t.sample > end_a.sample || sdckb_t.sample > end_a.sample) { | |
debug("Reached the end of the packet"); | |
return packet; | |
} | |
if (phase === 1) { | |
debug("IN Phase 1"); | |
// Find negative sdcka | |
if (sdcka_t.val === 0) { | |
if (!start) { | |
start = sdcka_t.sample; | |
} | |
bit = sample_val(ch_sdckb, sdcka_t.sample); | |
bits.push({ | |
channel: ch_sdcka, | |
sample: sdcka_t.sample, | |
value: bit | |
}); | |
count += 1; | |
sdckb_t = trs_go_after(ch_sdckb, sdcka_t.sample); | |
phase = 2; | |
} else { | |
next_sdcka(); | |
} | |
} else if (phase === 2) { | |
debug("IN Phase 2"); | |
// Find negative sdckb | |
if (sdckb_t.val === 0) { | |
bit = sample_val(ch_sdcka, sdckb_t.sample); | |
bits.push({ | |
channel: ch_sdckb, | |
sample: sdckb_t.sample, | |
value: bit | |
}); | |
count += 1; | |
if (count >= 8) { | |
debug("Byte found"); | |
end = sdckb_t.sample; | |
var bit_array = extract_bits(bits); | |
packet.push({ | |
start: start, | |
end: end, | |
bits: bits, | |
value: bit_array_to_byte(bit_array) | |
}); | |
bits = []; | |
count = 0; | |
start = null; | |
end = null; | |
} | |
sdcka_t = trs_go_after(ch_sdcka, sdckb_t.sample); | |
phase = 1; | |
} else { | |
next_sdckb(); | |
} | |
} | |
} | |
} | |
function find_start_frame() { | |
var start_a, end_a, count = 0; | |
while (trs_is_not_last(ch_sdcka) || trs_is_not_last(ch_sdckb)) { | |
if (abort_requested()) { | |
return null; | |
} | |
if (start_a) { | |
if (end_a) { | |
if (sdckb_t.sample >= end_a.sample) { | |
if (count === 0 || count === 1) { | |
// Nothing special ignore it | |
start_a = end_a = null; | |
count = 0; | |
} else if (count === 4 || count === 9 || count === 13) { | |
lable_start_frame(start_a.sample, end_a.sample, count); | |
return end_a; | |
} else { | |
lable_start_frame(start_a.sample, end_a.sample, count); | |
// Start frame error, keep going | |
start_a = end_a = null; | |
count = 0; | |
} | |
} else { | |
if (sdckb_t.val === 0) { | |
count += 1; | |
} | |
next_sdckb(); | |
} | |
} else { | |
end_a = sdcka_t; | |
sdckb_t = trs_go_after(ch_sdckb, start_a.sample); | |
} | |
} else { | |
if (sdcka_t.val === 0) { | |
// We caught the first negative edge | |
start_a = sdcka_t; | |
} | |
next_sdcka(); | |
} | |
} | |
return null; | |
} | |
function find_end_frame() { | |
var start_b, end_b, count = 0; | |
while (trs_is_not_last(ch_sdcka) || trs_is_not_last(ch_sdckb)) { | |
if (abort_requested()) { | |
return false; | |
} | |
debug ("Looking at sdcka: " + sdcka_t.sample + " sdckb: " + sdckb_t.sample); | |
if (start_b) { | |
if (end_b) { | |
if (sdcka_t.sample >= end_b.sample) { | |
debug("Out of frame with count: " + count); | |
if (count === 0 || count === 1) { | |
// Nothing special ignore it | |
start_b = end_b = null; | |
count = 0; | |
} else if (count === 2) { | |
debug("Found end frame!"); | |
lable_end_frame(start_b.sample, end_b.sample, count); | |
return start_b; | |
} else { | |
debug("Found a bad end frame"); | |
lable_end_frame(start_b.sample, end_b.sample, count); | |
// End frame error, stop | |
return null; | |
} | |
} else { | |
if (sdcka_t.val === 0) { | |
debug("Found falling edge on sdcka at " + sdcka_t.sample); | |
count += 1; | |
} | |
next_sdcka(); | |
} | |
} else { | |
end_b = sdckb_t; | |
debug("Found rising edge at " + end_b.sample); | |
sdcka_t = trs_go_after(ch_sdcka, start_b.sample); | |
} | |
} else { | |
debug("We are looking for a falling edge on sdckb"); | |
if (sdckb_t.val === 0) { | |
// We caught the first negative edge | |
start_b = sdckb_t; | |
debug("Found one at: " + sdckb_t.sample); | |
} | |
next_sdckb(); | |
} | |
} | |
debug("reached end of samples"); | |
return null; | |
} | |
function lable_start_frame(start_sample, end_sample, count) { | |
debug("Found start frame at: [" + start_sample + ", " + end_sample + "] with count: " + count); | |
dec_item_new(ch_sdckb, start_sample, end_sample); | |
if (count == 4) { | |
dec_item_add_pre_text("Start Frame"); | |
dec_item_add_pre_text("SF"); | |
} else if (count == 9) { | |
dec_item_add_pre_text("Start Frame w/ CRC"); | |
dec_item_add_pre_text("SF CRC"); | |
} else if (count == 13) { | |
dec_item_add_pre_text("Start Reset"); | |
dec_item_add_pre_text("RST"); | |
} else { | |
dec_item_add_pre_text("Frame Error"); | |
dec_item_add_pre_text("FE"); | |
} | |
} | |
function lable_end_frame(start_sample, end_sample, count) { | |
debug("Found end frame at: [" + start_sample + ", " + end_sample + "] with count: " + count); | |
dec_item_new(ch_sdcka, start_sample, end_sample); | |
if (count == 2) { | |
dec_item_add_pre_text("End Frame"); | |
dec_item_add_pre_text("EF"); | |
} else { | |
dec_item_add_pre_text("End Error"); | |
dec_item_add_pre_text("FE"); | |
} | |
} | |
function extract_bits(bit_samples) { | |
return bit_samples.map(function(sample){ return sample.value; }); | |
} | |
function bit_array_to_byte(bits) { | |
var value = 0; | |
for ( var i = 0; i < bits.length; ++i) { | |
value = (value << 1) + bits[i]; | |
} | |
return value; | |
} | |
/* | |
*/ | |
function int_to_str_hex (num) { | |
var temp = "0x"; | |
if (num < 0x10) | |
{ | |
temp += "0"; | |
} | |
temp += num.toString(16).toUpperCase(); | |
return temp; | |
} | |
/* | |
*/ | |
function check_scanastudio_support() { | |
if (typeof(pkt_start) != "undefined") | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
/* | |
*/ | |
function get_ch_light_color (k) | |
{ | |
var chColor = get_ch_color(k); | |
chColor.r = (chColor.r * 1 + 255 * 3) / 4; | |
chColor.g = (chColor.g * 1 + 255 * 3) / 4; | |
chColor.b = (chColor.b * 1 + 255 * 3) / 4; | |
return chColor; | |
} | |
/* | |
*/ | |
function get_bit_margin() | |
{ | |
var k = 5; | |
return ((k * sample_rate) / 10000000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment