a2_memory_map/
lib.rs

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
20/// All interactions are through this object
21pub 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    /// Borrow the underlying HashMap
84    pub fn get_all(&self) -> &HashMap<u16,AddressInfo> {
85        return &self.memmap;
86    }
87    /// Get data for one address.
88    /// @param addr address in the range -32767 to 65535
89    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    /// Get data for one address and split by context
98    /// @param addr Array of AddressInfo objects, one for each context, or undefined
99    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 one context do not split anything
107            if num == 1 {
108                ans.push(obj);
109                return Some(ans);
110            }
111            // if multi-context, split all fields on `|`
112            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}