1use serde_json;
2use std::collections::HashMap;
3
4#[cfg(windows)]
5const JSON_MAP: &str = include_str!("..\\..\\src\\map.json");
6#[cfg(not(windows))]
7const JSON_MAP: &str = include_str!("../../src/map.json");
8
9#[derive(Clone)]
10pub struct AddressInfo {
11 pub brief: Option<String>,
12 pub ctx: Option<String>,
13 pub desc: String,
14 pub label: Option<String>,
15 pub note: Option<String>,
16 pub subctx: Option<String>,
17 pub typ: String
18}
19
20pub struct MemoryMap {
22 memmap: HashMap<u16,AddressInfo>
23}
24
25fn get_val(key: &str,val: &serde_json::Value) -> Option<String> {
26 match &val[key] {
27 serde_json::Value::String(s) => Some(s.to_string()),
28 _ => None
29 }
30}
31fn get_contextual_str(str: &String, count: usize) -> String {
32 let contexts: Vec<_> = str.split('|').collect();
33 if count < contexts.len() {
34 return contexts[count].trim().to_string();
35 }
36 return str.to_string();
37}
38fn get_contextual_str_maybe(maybe_str: &Option<String>, count: usize) -> Option<String> {
39 if let Some(str) = maybe_str {
40 let contexts: Vec<_> = str.split('|').collect();
41 if count < contexts.len() {
42 return Some(contexts[count].trim().to_string());
43 }
44 }
45 return None;
46}
47fn get_contextual_info(info: &AddressInfo, count: usize) -> AddressInfo {
48 AddressInfo {
49 brief: get_contextual_str_maybe(&info.brief,count),
50 ctx: get_contextual_str_maybe(&info.ctx,count),
51 desc: get_contextual_str(&info.desc,count),
52 subctx: get_contextual_str_maybe(&info.subctx,count),
53 label: get_contextual_str_maybe(&info.label,count),
54 note: get_contextual_str_maybe(&info.note,count),
55 typ: get_contextual_str(&info.typ,count)
56 }
57}
58
59impl MemoryMap {
60 pub fn new() -> Self {
61 let mut m = Self {
62 memmap: HashMap::new()
63 };
64 let temp = serde_json::from_str::<serde_json::Value>(JSON_MAP).unwrap();
65 if let Some(obj) = temp.as_object() {
66 for (key,val) in obj {
67 let addr = u16::from_str_radix(&key[2..],16).expect("bad address in memory map");
68 let addr_info = AddressInfo {
69 brief: get_val("brief",val),
70 ctx: get_val("ctx",val),
71 desc: get_val("desc",val).unwrap(),
72 subctx: get_val("subctx",val),
73 label: get_val("label",val),
74 note: get_val("note",val),
75 typ: get_val("type",val).unwrap(),
76 };
77 m.memmap.insert(addr,addr_info);
78 }
79 }
80 assert!(m.memmap.len()>0);
81 m
82 }
83 pub fn get_all(&self) -> &HashMap<u16,AddressInfo> {
85 return &self.memmap;
86 }
87 pub fn get_one(&self,addr: i64) -> Option<AddressInfo> {
90 let pos_addr = match addr < 0 { true => addr + 1 + u16::MAX as i64, false => addr };
91 if pos_addr >=0 && pos_addr < u16::MAX as i64 {
92 self.memmap.get(&(pos_addr as u16)).cloned()
93 } else {
94 None
95 }
96 }
97 pub fn get_one_and_split(&self,addr: i64) -> Option<Vec<AddressInfo>> {
100 if let Some(obj) = self.get_one(addr) {
101 let mut ans = Vec::new();
102 let num = match &obj.ctx {
103 Some(ctx) => ctx.split('|').collect::<Vec<_>>().len(),
104 None => 1
105 };
106 if num == 1 {
108 ans.push(obj);
109 return Some(ans);
110 }
111 for count in 0..num {
113 ans.push(get_contextual_info(&obj, count));
114 }
115 return Some(ans);
116 }
117 None
118 }
119}
120
121#[test]
122fn get_cout() {
123 let mem_map = MemoryMap::new();
124 let cout = mem_map.get_one(0xfded);
125 assert!(cout.unwrap().label==Some("COUT".to_string()));
126}
127
128#[test]
129fn applesoft_integer() {
130 let mem_map = MemoryMap::new();
131 let addr = 0xf8;
132 match mem_map.get_one_and_split(addr) {
133 Some(info_list) => {
134 assert_eq!(info_list.len(),2);
135 assert_eq!(info_list[0].ctx,Some("Applesoft".to_string()));
136 assert_eq!(info_list[1].ctx,Some("Integer BASIC".to_string()));
137 assert_eq!(info_list[0].typ,"byte value".to_string());
138 assert_eq!(info_list[1].typ,"float32".to_string());
139 assert_eq!(info_list[0].label,Some("REMSTK".to_string()));
140 assert_eq!(info_list[1].label,Some("FP1".to_string()));
141 },
142 None => panic!("address not found")
143 }
144 let addr = 0xdc;
145 match mem_map.get_one_and_split(addr) {
146 Some(info_list) => {
147 assert_eq!(info_list.len(),2);
148 assert_eq!(info_list[0].ctx,Some("Applesoft".to_string()));
149 assert_eq!(info_list[1].ctx,Some("Integer BASIC".to_string()));
150 assert_eq!(info_list[0].typ,"word".to_string());
151 assert_eq!(info_list[1].typ,"word".to_string());
152 assert_eq!(info_list[0].label,Some("ERRPOS".to_string()));
153 assert_eq!(info_list[1].label,Some("CURLIN".to_string()));
154 },
155 None => panic!("address not found")
156 }
157}
158
159#[test]
160fn no_split() {
161 let mem_map = MemoryMap::new();
162 let addr = 0xff70;
163 match mem_map.get_one_and_split(addr) {
164 Some(info_list) => {
165 assert_eq!(info_list.len(),1);
166 assert_eq!(info_list[0].typ,"ROM routine".to_string());
167 },
168 None => panic!("address not found")
169 }
170 let addr = 0xe597;
171 match mem_map.get_one_and_split(addr) {
172 Some(info_list) => {
173 assert_eq!(info_list.len(),1);
174 assert_eq!(info_list[0].typ,"ROM routine".to_string());
175 assert_eq!(info_list[0].subctx,Some("parsing & string".to_string()));
176 },
177 None => panic!("address not found")
178 }
179}
180
181#[test]
182fn splits_but_one_ctx() {
183 let mem_map = MemoryMap::new();
184 let addr = 0xc080;
185 match mem_map.get_one_and_split(addr) {
186 Some(info_list) => {
187 assert_eq!(info_list.len(),1);
188 assert_eq!(info_list[0].brief,Some("RDRAM | stepper 0".to_string()));
189 },
190 None => panic!("address not found")
191 }
192}