1// ââââââ âââââ âââ âââ ââââââ âââââ ââââââ ââââââ 2// ââ ââ ââ ââ ââââ ââââ ââ ââ ââ ââ ââ ââ ââ 3// ââââââ âââââââ ââ ââââ ââ ââ âââââââ ââââââ ââ ââ 4// ââ ââ ââ ââ ââ ââ ââ ââ ââ ââ ââ ââ ââ ââ 5// ââ ââ ââ ââ ââ ââ ââââââ ââ ââ ââ ââ ââââââ 6// 7// Copyright (c) 2026 Freddy Vandriessche. 8// notice: https://raw.githubusercontent.com/RetroAppleJS/RetroAppleJS.github.io/main/LICENSE.md 9// 10// EMU_CARD_ramcard.js 11 12// Discovery container. Apple2IO mounts a separate live instance. 13if(oEMU===undefined) var oEMU = {"component":{"IO":{"RamCard":new RamCard()}}} 14else oEMU.component.IO.RamCard = new RamCard(); 15 16function RamCard() 17{ 18 this.id = {"PCODE":"MS16K", "icon":"fa fa-microchip"} 19 this.state = { 20 "active":true // flag to remember if peripheral is altogether operational or not 21 ,"softswitch_pos":0x1 // default soft switch state (documented by microsoft) 22 ,"bMapped":false // flag to remember if onboard ROM is mapped by the language card (true) or not (false) 23 ,"RR":1 // previous qualifying access / double-trigger latch 24 }; 25 //this.action = {"SlotIO": { "RD":{ "callback": function(addr) { return card.soft_switch(addr); } } } }; // generic callback for softswitches 26 27 this.action = 28 { 29 "SlotIO": 30 { 31 "RD": { "callback": function(addr,ctx) { return card.soft_switch(addr,ctx); } }, 32 "WR": { "callback": function(addr,d8,ctx) { return card.soft_switch(addr,ctx); } } 33 } 34 }; // generic callback for language-card softswitches; reads and writes both trigger them 35 36 var bDebug_sw = false; // debug soft switch updates (light) 37 var bDebug_mon = false; // debug RAM Write monitor 38 var bDebug = false; // debug all RAM R/W operations 39 40 const ROM_ID = _CFG_SYSCODE["A2P"].ROM; 41 const ROM_CFG = _CFG_ROMRANGES[ROM_ID]; 42 const ROM_RANGE = oCOM.parseRngExpr(ROM_CFG.ROM); 43 44 const BANK_SIZE = 0x1000; // 4K bank memory size = contiguous bank offset = 4Kbytes 45 const CONT_SIZE = 0x2000; // 8K contiguous memory size 46 const BANK_TOT_SIZE = BANK_SIZE * 2; // 2*4K linearised bank size 47 const TOTAL_SIZE = BANK_TOT_SIZE + CONT_SIZE; // 16K 48 49 var RAMCARD_MEM = new Uint8Array(TOTAL_SIZE); // PERIPHERAL RAM 50 51 var hw; // purposed for late-binding (at restart) 52 var io; // purposed for late-binding (at restart) 53 var card = this; // stand-in in areas where 'this' is absent e.g. inside mapping functions 54 55 // RE: 0="READ-ENABLE" 1="READ-ENABLE" | WE: 0:"WRITE-PROTECT" 1:"WRITE-ENABLE" | BANK: 0="BANK A", 1="BANK B" 56 var softswitch = { 57 0x0: {"RE":1, "BANK":0} // READ: $D000-$DFFF -> BANK_MEM[1] | READ: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 58 ,0x1: { "WE":1, "BANK":0} // READ: $D000-$DFFF & $E000-$EFFF & $F000-$FFFF -> apple2Rom | WRITE $D000-$DFFF -> BANK_MEM[1] | WRITE: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 59 ,0x2: { "BANK":0} // READ: $D000-$DFFF & $E000-$EFFF & $F000-$FFFF -> apple2Rom 60 ,0x3: {"RE":1, "WE":1, "BANK":0} // READ: $D000-$DFFF -> BANK_MEM[1] | READ: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM | WRITE $D000-$DFFF -> BANK_MEM[1] | WRITE: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 61 62 ,0x8: {"RE":1, "BANK":1} // READ: $D000-$DFFF -> BANK_MEM[0] | READ: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 63 ,0x9: { "WE":1, "BANK":1} // READ: $D000-$DFFF & $E000-$EFFF & $F000-$FFFF -> apple2Rom | WRITE $D000-$DFFF -> BANK_MEM[0] | WRITE: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 64 ,0xA: { "BANK":1} // READ: $D000-$DFFF & $E000-$EFFF & $F000-$FFFF -> apple2Rom 65 ,0xB: {"RE":1, "WE":1, "BANK":1} // READ: $D000-$DFFF ->
65 BANK_MEM[0] | READ: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM | WRITE $D000-$DFFF -> BANK_MEM[0] | WRITE: $E000-$EFFF & $F000-$FFFF -> RAMCARD_MEM 66 } 67 var softswitch_diff = {RE:0,WE:0,BANK:0}; 68 69 this.mapRead = function(addr) 70 { 71 return card.read(abs2rel(addr)); 72 }; 73 74 this.mapWrite = function(addr,d8) 75 { 76 var rel_addr = abs2rel(addr); 77 var sw = softswitch[card.state.softswitch_pos] || {}; 78 79 card.write(rel_addr,d8); 80 card.mark_MEM_monitoring( 81 rel_addr<BANK_SIZE ? (sw.BANK==0 ? "bankA" : "bankB") : "cont", 82 rel_addr 83 ); 84 }; 85 86 this.updateMemoryMap = function(bRamcardActive) 87 { 88 if(hw===undefined) { hw = apple2plus.hwObj(); console.warn("EMU_CARD_ramcard.js lost hw from scope") } 89 if(io===undefined) { io = apple2plus.hwObj().io; console.warn("EMU_CARD_ramcard.js lost io from scope") } 90 91 if(this.bMEM_monitoring != hw.bMEM_monitoring) 92 this.enable_MEM_monitoring(hw.bMEM_monitoring); // follow mem monitoring flag from hardware 93 if(bDebug_sw) 94 95 debug_updateMemoryMap(bRamcardActive,this); // log memory map updates 96 if(!io || !io.MEMORY_MAP || !this.mount) return false; 97 98 var sw = softswitch[this.state.softswitch_pos] || {}; 99 var ruleID = this.id.PCODE+":"+this.mount.hash; 100 var ok = io.MEMORY_MAP.runRule(ruleID,{ 101 "position":"$"+oCOM.getHexByte(this.state.softswitch_pos) 102 ,"RE":sw.RE==1 103 ,"WE":sw.WE==1 104 ,"RR":this.state.RR==1 105 ,"BANK":sw.BANK==0 ? "A" : "B" 106 ,"mapped":!!bRamcardActive 107 }); 108 109 if(ok) this.state.bMapped = !!bRamcardActive; 110 return ok; 111 }; 112 113 this.reset = function() 114 { 115 // Microsoft RAMCard RESET state is equivalent to two accesses of $C081: 116 // BANK A internally, ROM readable, LC RAM writable. 117 this.state.softswitch_pos = 0x1; 118 this.state.bMapped = false; 119 this.state.RR = 1; 120 if(io && this.mount) this.updateMemoryMap(false); 121 }; 122 123 this.restart = function() 124 { 125 hw = apple2plus.hwObj(); 126 io = apple2plus.hwObj().io; 127 debug_flush(); 128 this.state.softswitch_pos = 0x1; 129 this.state.bMapped = false; 130 this.state.RR = 1; 131 132 var ruleID = this.id.PCODE+":"+this.mount.hash; 133 io.MEMORY_MAP.addRule(ruleID,function(state) 134 { 135 var writeEnabled = state.WE && state.RR; 136 return { 137 "owner":card 138 ,"source":"MS16K language-card soft switches" 139 ,"state":state 140 ,"mappings":[ 141 { 142 "id":"upper-memory-read" 143 ,"space":ROM_ID 144 ,"op":"RD" 145 ,"range":ROM_CFG.ROM 146 ,"handler":state.mapped ? "mapRead" : "@default" 147 ,"target":state.mapped ? "MS16K RAM" : "Apple II ROM" 148 ,"enabled":true 149 ,"condition":"RE="+Number(state.RE)+"; mapped="+Number(state.mapped)+"; bank="+state.BANK 150 }, 151 { 152 "id":"upper-memory-write" 153 ,"space":ROM_ID 154 ,"op":"WR" 155 ,"range":ROM_CFG.ROM 156 ,"handler":"mapWrite" 157 ,"target":"MS16K RAM - bank "+state.BANK+" and contiguous 8K" 158 ,"enabled":writeEnabled 159 ,"condition":"WE="+Number(state.WE)+"; RR="+Number(state.RR)+"; bank="+state.BANK 160 } 161 ] 162 }; 163 }); 164 165 // Register the initial ROM-read/RAM-write routing and its evidence. 166 this.updateMemoryMap(false); 167 168 169 var ramcard = apple2plus.hwObj().io.HASH2obj( this.mount.hash ); // reference the ramcard object by hash 170 oCOM.addRefreshEvent(ramcard.MEM_monitoring,"MEM_monitoring_MS16K",false); // enabled together with the main memory monitor by the UI button 171 } 172 173 function abs2rel(addr) { return ((addr & 0xFFFF) - ROM_RANGE.from); } 174 function rel2abs(rel_addr) { return (rel_addr + ROM_RANGE.from) & 0xFFFF; } 175 176 // translate addresses from the bus to ramcard addresses 177 function ramcard_address_encoder(addr,bank_bit) 178 { 179 return addr + ((bank_bit | !!(addr >> 12)) << 12); 180 } 181 182 this.soft_switch = function(rel_io_addr) 183 { 184 if(io===undefined) { io = apple2plus.hwObj().io;
184 console.warn("EMU_CARD_ramcard.js lost io from scope") } 185 186 const slot = 0; // TODO: take fromthis.state.slot 187 const pre_sw = softswitch[sw_mask(this.state.softswitch_pos)]; // previous switch state 188 const status_nibble = (pre_sw.BANK==1 ? 1 : 0) | (pre_sw.RE==1 ? 2 : 0) | (pre_sw.WE==1 ? 4 : 0) | (this.state.RR==1 ? 8 : 0); 189 if(apple2plus.hwObj().bRO == true) return status_nibble; // skip soft switch manupulation; just return status when hardware is in Read-Only mode 190 191 this.state.softswitch_pos = sw_mask(io.address_encoder(rel_io_addr,"SlotIO",slot)); // set softswitch position 192 const sw = softswitch[this.state.softswitch_pos] || {}; mon_soft_switch(sw); // access soft switch 193 debug_flush(); if(bDebug_sw) debug_soft_switch(sw,this.state); 194 195 /* 196 * RR represents whether the PREVIOUS LC control access qualified for 197 * write enable. updateMemoryMap() must therefore see RR before this 198 * access changes it: 199 * 200 * first odd access: WE=1, RR=0 -> writes remain protected 201 * second odd access: WE=1, RR=1 -> writes become enabled 202 * 203 * An even/write-protect access clears RR for the following access. 204 * Read mapping (RE) is independent and takes effect immediately. 205 */ 206 var ok = this.updateMemoryMap(!!sw.RE); 207 this.state.RR = sw.WE==1 ? 1 : 0; 208 209 if(bDebug_sw && !ok) console.warn(this.id.PCODE+": SOFTSWITCH could not update memory map"); 210 211 212 // MEM_monitoring() updates the soft-switch display only while monitoring is enabled. 213 return status_nibble; 214 }; 215 216 // mask io address (Ramcard specifically ignores bit 2, e.g. $C085 -> hits soft switch 0x1, as 0x5 is not a distinct soft switch) 217 function sw_mask(sw) { return sw & 0b1011; } 218 219 220 this.read = function(rel_addr) 221 { 222 var sw = softswitch[this.state.softswitch_pos] || {}; 223 const d8 = RAMCARD_MEM[ ramcard_address_encoder(rel_addr,sw.BANK) ]; 224 if(apple2plus.hwObj().bRO != true) 225 { 226 mon_soft_switch(sw); 227 if(bDebug) debug_record( rel_addr < BANK_SIZE ? DBG_READ_BANK : DBG_READ_RAMCARD, rel_addr, d8, rel_addr < BANK_SIZE ? (sw.BANK==0 ? "A" : "B") : null ); 228 } 229 return d8; 230 }; 231 232 this.write = function(rel_addr,d8) 233 { 234 var sw = softswitch[this.state.softswitch_pos] || {}; mon_soft_switch(sw); 235 if(sw.WE==1 && this.state.RR==1) 236 { 237 RAMCARD_MEM[ ramcard_address_encoder(rel_addr,sw.BANK) ] = d8; 238 if(bDebug) debug_record( rel_addr < BANK_SIZE ? DBG_WRITE_BANK : DBG_WRITE_RAMCARD, rel_addr, d8, rel_addr < BANK_SIZE ? (sw.BANK==0 ? "A" : "B") : null ); 239 } 240 else 241 { 242 debug_flush(); 243 console.warn(this.id.PCODE+": FAILED ATTEMPT: (WE="+(sw.WE==1?true:false)+") BANK"+(sw.BANK==0?"A":"B")+" write #$"+oCOM.getHexByte(d8)+" at addr $"+oCOM.getHexWord(rel2abs(rel_addr))); 244 } 245 }; 246 247 248 // ______ _____ _____ _____ __ __ 249 // .' ___ ||_ _||_ _||_ _| [ | [ | 250 // / .' \_| | | | | | | | |--. .---. | | _ .--. .---. _ .--. .--. 251 // | | ____ | ' ' | | | | .-. |/ /__\\ | |[ '/'`\ \/ /__\\[ `/'`\]( (`\] 252 // \ `.___] | \ \__/ / _| |_ | | | || \__., | | | \__/ || \__., | | `'.'. 253 // `._____.' `.__.' |_____| [___]|__]'.__.'[___]| ;.__/ '.__.'[___] [\__) ) 254 // [__| 255 256 this.MEM_status_id = this.id.PCODE+"_softswitch_status"; 257 this.mem_mon = {"cont":{}, "bankA":{}, "bankB":{}}; 258 this.bMEM_monitoring = false; 259 this.MEM_grid_cnf = {"id_prefix":"x","digits":5}; 260 this.mem_layout = 261 { 262 "3F000-40000":["#D06060",this.id.PCODE+" CONT" ,"RF"] 263 ,"3E000-3F000":["#D06060",this.id.PCODE+" CONT" ,"RE"] 264 ,"2D000-2E000":["#B05050",this.id.PCODE+" BANK B","DB"] 265 ,"1D000-1E000":["#A04040",this.id.PCODE+" BANK A","DA"] 266 }; 267 this.MEM_grid = 268 {
269 "cont": {"cnf":{"id_prefix":"x","digits":4},"layout":{ "F000-FFFF":["#D06060",this.id.PCODE+ "CONT" ,"RF"] 270 ,"E000-FFFF":["#D06060",this.id.PCODE+" CONT" ,"RE"] }}, 271 "bankB": {"cnf":{"id_prefix":"z","digits":4},"layout":{ "D000-DFFF":["#B05050",this.id.PCODE+" BANK B","DB"] }}, 272 "bankA": {"cnf":{"id_prefix":"y","digits":4},"layout":{ "D000-DFFF":["#A04040",this.id.PCODE+" BANK A","DA"] }} 273 }; 274 var tm = Number(new Date()); 275 this.MEM_status_text = function() 276 { 277 var on_icon = (softswitch_diff.RE>0) ? '<i class="fa fa-microchip" title="'+this.id.PCODE+' RAM"></i>' : '<i class="fa fa-apple-alt" title="'+this.id.PCODE+' ROM"></i>'; 278 var we_icon = (softswitch_diff.WE>0) ? '<i class="fa fa-lock-open" title="'+this.id.PCODE+' WRITE ENABLE"></i>': '<i class="fa fa-lock" title="'+this.id.PCODE+' WRITE PROTECTED"></i>'; 279 var bank = (softswitch_diff.BANK>0) ? '<span title="'+this.id.PCODE+' BANK B">B</span>' : '<span title="'+this.id.PCODE+' BANK A">A</span>'; 280 281 // debug 282 //on_icon += " "+softswitch_diff.RE; 283 //we_icon += " "+softswitch_diff.WE; 284 //bank += " "+softswitch_diff.BANK; 285 286 softswitch_diff = {RE:0,WE:0,BANK:0}; 287 return this.id.PCODE+' ' + on_icon + ' ' + we_icon + ' ' + bank; 288 }; 289 this.update_MEM_status = function() 290 { 291 if(typeof(document)!="object") return false; 292 var el = document.getElementById(this.MEM_status_id); if(!el) return false; 293 el.innerHTML = this.MEM_status_text(); 294 return true; 295 }; 296 this.build_MEM_map = function(linestep) 297 { 298 linestep = linestep || 0x1000; 299 return "<div style='display:flex;flex-direction:column;gap:6px;align-items:flex-start'>" 300 + oMEMGRID.build_grid(0xF000,2,-linestep,{id_prefix:"x",digits:4,table_id:"xEF"}) 301 + oMEMGRID.build_grid(0xD000,1,-linestep,{id_prefix:"z",digits:4,table_id:"zD"}) 302 + oMEMGRID.build_grid(0xD000,1,-linestep,{id_prefix:"y",digits:4,table_id:"yD"}) 303 + "<div id='"+this.MEM_status_id+"' style='float:right;margin-left:30px;padding:2px;height:20px;margin-top:-6px;background-color:white;border-radius:5px'>" 304 + this.MEM_status_text() 305 + "</div>" 306 + "</div>"; 307 }; 308 this.paint_MEM_map = function() 309 { 310 if(!this.MEM_grid) return false; 311 312 oMEMGRID.relabel_grid_rows("xEF",["F000Â Â Â Â ","E000Â Â Â Â "]); 313 oMEMGRID.relabel_grid_rows("zD",["D000-B"]); 314 oMEMGRID.relabel_grid_rows("yD",["D000-A"]); 315 oMEMGRID.paint_grid(this.MEM_grid.cont.layout, this.MEM_grid.cont.cnf); 316 oMEMGRID.paint_grid(this.MEM_grid.bankB.layout, this.MEM_grid.bankB.cnf); 317 oMEMGRID.paint_grid(this.MEM_grid.bankA.layout, this.MEM_grid.bankA.cnf); 318 this.update_MEM_status(); 319 320 return true; 321 }; 322 323 // mark one block in the memory grid 324 this.mark_MEM_monitoring = function(bucket, rel_addr) 325 { 326 if(!this.bMEM_monitoring) return; 327 if(!this.mem_mon[bucket]) this.mem_mon[bucket] = {}; 328 329 this.mem_mon[bucket][rel2abs(rel_addr) >> oMEMGRID.mem_gran] = true; 330 331 if(hw && !hw.mem_mon_trigger[this.id.PCODE]) 332 { 333 this.MEM_monitoring(); 334 hw.mem_mon_trigger[this.id.PCODE] = true; 335 } 336 }; 337 338 // clear all blocks in the memory grid 339 this.reset_MEM_monitoring = function() 340 { 341 if(bDebug_mon) console.log('MEM_monitoring_'+this.id.PCODE+' -> reset_MEM_monitoring()'); 342 this.mem_mon = {"cont":{}, "bankA":{}, "bankB":{}}; 343 if(this.MEM_grid) 344 { 345 oMEMGRID.paint_grid(this.MEM_grid.cont.layout, this.MEM_grid.cont.cnf); 346 oMEMGRID.paint_grid(this.MEM_grid.bankB.layout,this.MEM_grid.bankB.cnf); 347 oMEMGRID.paint_grid(this.MEM_grid.bankA.layout,this.MEM_grid.bankA.cnf); 348 } 349 this.update_MEM_status(); 350 } 351 352 // enable/disable monitoring - actioned by a UI button 353 this.enable_MEM_monitoring = function(b) 354 { 355 if(bDebug_mon) console.log('MEM_monitoring_'+this.id.PCODE+' -> enable_MEM_monitoring('+b+')'); 356 this.bMEM_monitoring = !!b; 357 if(b) this.reset_MEM_monitoring(); // clear data 358 } 359 360 // lights up memory blocks in the grid 361 // called regularly as the result of oCOM.addRefreshEvent (e.g. 2 times per second) 362 this.MEM_monitoring = function() 363 { 364 if(!(this.state && this.MEM_grid)) return; 365 this.update_MEM_status(); 366 if(bDebug_mon) console.log(JSON.stringify( 'MEM_monitoring_'+this.id.PCODE+' ->
366 bankA:'+JSON.stringify(this.mem_mon.bankA)+' bankB:'+JSON.stringify(this.mem_mon.bankB)+' cont:'+JSON.stringify(this.mem_mon.cont)) ); 367 368 if(Object.keys(this.mem_mon.cont).length>0) // is there anything to display? 369 { 370 oMEMGRID.paint_grid(this.MEM_grid.cont.layout, this.MEM_grid.cont.cnf); 371 oMEMGRID.update_grid(this.mem_mon.cont, this.MEM_grid.cont.cnf); 372 } 373 if(Object.keys(this.mem_mon.bankB).length>0) // is there anything to display? 374 { 375 oMEMGRID.paint_grid(this.MEM_grid.bankB.layout, this.MEM_grid.bankB.cnf); 376 oMEMGRID.update_grid(this.mem_mon.bankB, this.MEM_grid.bankB.cnf); 377 } 378 379 if(Object.keys(this.mem_mon.bankA).length>0) // is there anything to display? 380 { 381 oMEMGRID.paint_grid(this.MEM_grid.bankA.layout, this.MEM_grid.bankA.cnf); 382 oMEMGRID.update_grid(this.mem_mon.bankA, this.MEM_grid.bankA.cnf); 383 } 384 385 if(hw.bClear_mon) this.mem_mon = {"cont":{}, "bankA":{}, "bankB":{}}; // CLEAR THE GRID AFTER EACH DISPLAY 386 } 387 388 // Vote on ramcard status (since softswitches change more rapidly than visual updates, we want to show the most prevalent state) 389 function mon_soft_switch(sw) 390 { 391 softswitch_diff.RE += sw.RE==1 ? 1 : -1; 392 softswitch_diff.WE += (sw.WE==1 && card.state.RR==1) ? 1 : -1; 393 softswitch_diff.BANK += sw.BANK==1 ? 1 : -1; 394 } 395 396 397 // _____ ___ ______ __ __ 398 // |_ _| .' `. .' ___ | [ | [ | 399 // | | / .-. \/ .' \_| | |--. .---. | | _ .--. .---. _ .--. .--. 400 // | | _ | | | || | ____ | .-. |/ /__\\ | |[ '/'`\ \/ /__\\[ `/'`\]( (`\] 401 // _| |__/ |\ `-' /\ `.___] | | | | || \__., | | | \__/ || \__., | | `'.'. 402 // |________| `.___.' `._____.' [___]|__]'.__.'[___]| ;.__/ '.__.'[___] [\__) ) 403 // [__| 404 405 var dbgCurrent = null; 406 var logStep = 0; 407 408 var DBG_READ_BANK = "READ RAM BANK "; 409 var DBG_READ_RAMCARD = "READ RAM CONT "; 410 var DBG_WRITE_BANK = "WRITE RAM BANK "; 411 var DBG_WRITE_RAMCARD = "WRITE RAM CONT "; 412 413 var dbgOps = {}; 414 dbgOps[DBG_READ_BANK] = dbg_op(DBG_READ_BANK); 415 dbgOps[DBG_READ_RAMCARD] = dbg_op(DBG_READ_RAMCARD); 416 dbgOps[DBG_WRITE_BANK] = dbg_op(DBG_WRITE_BANK); 417 dbgOps[DBG_WRITE_RAMCARD] = dbg_op(DBG_WRITE_RAMCARD); 418 const PCODE = this.id.PCODE; 419 420 function dbg_op(label) 421 { 422 return { 423 "label": label, 424 "bytes": 0, 425 "checksum": new Uint16Array(1), 426 "firstAddr": null, 427 "lastAddr": null, 428 "suffix": "" 429 }; 430 } 431 432 function debug_record(op, addr, d8, suffix) 433 { 434 suffix = suffix || ""; 435 if(dbgCurrent && (dbgCurrent != op || dbgOps[dbgCurrent].suffix != suffix)) debug_flush(); 436 var o = dbgOps[op]; 437 if(!o) return; 438 if(!dbgCurrent) 439 { 440 dbgCurrent = op; 441 o.firstAddr = addr; 442 o.suffix = suffix; 443 } 444 o.bytes++; 445 o.lastAddr = addr; 446 o.checksum[0] = (o.checksum[0] + ((addr & 0xFFFF) ^ (d8 & 0xFF))) & 0xFFFF; // Simple 16-bit rolling checksum over address and data. 447 } 448 449 function debug_flush() 450 { 451 if(!bDebug || !dbgCurrent) return; 452 var o = dbgOps[dbgCurrent]; 453 if(o && o.bytes) 454 { 455 console.log( 456 PCODE+": "+o.label + o.suffix + 457 " bytes=" + o.bytes + 458 " range=$" + oCOM.getHexWord(o.firstAddr) + "-$" + oCOM.getHexWord(o.lastAddr) + 459 " checksum=$" + oCOM.getHexWord(o.checksum[0]) 460 ); 461 } 462 o.bytes = 0; 463 o.checksum[0] = 0; 464 o.firstAddr = null; 465 o.lastAddr = null; 466 o.suffix = ""; 467 dbgCurrent = null; 468 } 469 470 function debug_updateMemoryMap(bRamcardActive) 471 { 472 console.log(PCODE+": updateMemoryMap() -> SWITCH "+(bRamcardActive?"ON":"OFF")); 473 if(bRamcardActive == false) 474 { 475 console.log( PCODE+": BANK A: 0x"+oCOM.crc16(RAMCARD_MEM.slice(0, BANK_SIZE)).toString(16).toUpperCase() 476 +" BANK B: 0x"+oCOM.crc16(RAMCARD_MEM.slice(BANK_SIZE, BANK_TOT_SIZE)).toString(16).toUpperCase() 477 +" CONT: 0x"+oCOM.crc16(RAMCARD_MEM.slice(BANK_TOT_SIZE, TOTAL_SIZE)).toString(16).toUpperCase() 478 +" STEP "+(logStep++)); 479 } 480 } 481 482 function debug_soft_switch(sw,state) 483 { 484 var addr = state.softswitch_pos; 485 if(sw.WE==1) 486 { 487 if(state.RR==1) console.log(PCODE+": SOFTSWITCH $"+oCOM.getHexByte(addr)+" -> "+JSON.stringify(sw)); 488 else console.log(PCODE+": waiting for RR to write-enable ($"+oCOM.getHexByte(addr)+")"); 489 } 490 else console.log(PCODE+": SOFTSWITCH $"+oCOM.getHexByte(addr)+" -> "+JSON.stringify(sw)); 491 } 492 493 this.deviceToolSlotHTML = function(ctx) 494 { 495 ctx = ctx || {}; 496 var slotN = Number(ctx.slotN); 497 return "" 498 + "<div class=toolbox id=\""+(ctx.toolboxID || ("device_tool_"+ctx.slotID))+"\" hidden>" 499 + " <div class=appbox style=\"height:76px;padding:0px 6px 0px 6px;\" title=\"Memory map\">" 500 + " <div style=\"float:left;width:28px;text-align:center\">MEM<br><button class=appbut><i class=\"fa fa-sync-alt\" id=\"MEM_monitoring\" onclick=\"" 501 + "oCOM.POPUP.toggle_class(this,'fa-stop-circle','fa-sync-alt');" 502 + "var bMEM_monitoring=oCOM.toggleRefreshEvent('MEM_monitoring');" 503 + "apple2plus.hwObj().enable_MEM_monitoring(bMEM_monitoring);" 504 + "oCOM.enableRefreshEvent('MEM_monitoring_MS16K',bMEM_monitoring);" 505 + "apple2plus.hwObj().io.SLOT2obj("+slotN+")?.enable_MEM_monitoring(bMEM_monitoring);" 506 + "\"></i></button><br>" 507 + " <button class=appbut id=\"RAM64K_import\"" 508 + " title=\"Load ram-64k.bin into Apple II main RAM $0000-$BFFF\"" 509 + " onclick=\"document.getElementById('RAM64K_file').
509click()\">" 510 + " <i class=\"fa fa-cloud-upload-alt\"></i></button>" 511 + " <input id=\"RAM64K_file\" type=\"file\" accept=\".bin,application/octet-stream\" hidden onchange=\"EMU_loadRAM64KFile(this)\"></div>" 512 + " <div id=\"EMU_mem_map\" style=\"margin-left:30px;white-space:nowrap\"></div>" 513 + " </div>" 514 + "</div>"; 515 }; 516 517} 518 519 520 521 522/* EXAMPLE CODE 523 524M.PRNTYX EQU $F940 ; print hex word (Y=hi X=lo) 525M.OUTSP EQU $DB57 ; space 526 527ORG $6000 528 529; WRITE BANK2 and RAMCARD in mode 3 :o) 530LDA $C083 ; 3: {"RE":true,"WE":true,"BANK":2} 531LDA $C083 ; 3: {"RE":true,"WE":true,"BANK":2} 532LDA #$12 533STA $E000 ; WRITE IN RAMCARD 534LDA #$34 535STA $D000 ; WRITE IN BANK 2 536LDX $D000 537LDY $E000 538LDA $C081 ; 1: {"WE":true,"BANK":2} 539LDA $C081 ; 1: {"WE":true,"BANK":2} 540JSR M.PRNTYX 541JSR M.OUTSP 542 543; WRITE BANK2 and RAMCARD in mode 2 :o( 544LDA $C082 ; 2: {"BANK":2} 545LDA #$23 546STA $E000 ; WRITE IN RAMCARD 547LDA #$45 548STA $D000 ; WRITE IN BANK 2 549LDX $D000 550LDY $E000 551LDA $C081 ; 1: {"WE":true,"BANK":2} 552LDA $C081 ; 1: {"WE":true,"BANK":2} 553JSR M.PRNTYX 554JSR M.OUTSP 555 556; WRITE BANK2 and RAMCARD in mode 1 :o) 557LDA $C081 ; 1: {"WE":true,"BANK":2} 558LDA $C081 ; 1: {"WE":true,"BANK":2} 559LDA #$34 560STA $E000 ; WRITE IN RAMCARD 561LDA #$56 562STA $D000 ; WRITE IN BANK 2 563LDX $D000 564LDY $E000 565JSR M.PRNTYX 566JSR M.OUTSP 567 568; WRITE BANK2 and RAMCARD in mode 0 :o( 569LDA $C080 ; 0: {"RE":true,"BANK":2} 570LDA #$45 571STA $D000 ; WRITE IN BANK 2 572LDA #$67 573STA $E000 ; WRITE IN RAMCARD 574LDX $D000 575LDY $E000 576LDA $C081 ; 1: {"WE":true,"BANK":2} 577LDA $C081 ; 1: {"WE":true,"BANK":2} 578JSR M.PRNTYX 579JSR M.OUTSP 580 581RTS 582 583 584CALL-151 5856000: AD 83 C0 AD 83 C0 A9 12 5866008: 8D 00 E0 A9 34 8D 00 D0 5876010: AE 00 D0 AC 00 E0 AD 81 5886018: C0 AD 81 C0 20 40 F9 20 5896020: 57 DB AD 82 C0 A9 23 8D 5906028: 00 E0 A9 45 8D 00 D0 AE 5916030: 00 D0 AC 00 E0 AD 81 C0 5926038: AD 81 C0 20 40 F9 20 57 5936040: DB AD 81 C0 AD 81 C0 A9 5946048: 34 8D 00 E0 A9 56 8D 00 5956050: D0 AE 00 D0 AC 00 E0 20 5966058: 40 F9 20 57 DB AD 80 C0 5976060: A9 45 8D 00 D0 A9 67 8D 5986068: 00 E0 AE 00 D0 AC 00 E0 5996070: AD 81 C0 AD 81 C0 20 40 6006078: F9 20 57 DB 60 601*/
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.