arm: added serial console, SerialIOInterface needs refactoring; moved VirtualTextScroller
This commit is contained in:
parent
240578d942
commit
a7f62079db
|
@ -1,6 +1,6 @@
|
|||
|
||||
import { RAM, RasterVideo, KeyFlags, dumpRAM, AnimationTimer, setKeyboardFromMap, padBytes, ControllerPoller } from "./emu";
|
||||
import { hex, printFlags, invertMap, getBasePlatform } from "./util";
|
||||
import { hex, printFlags, invertMap, getBasePlatform, byteToASCII } from "./util";
|
||||
import { CodeAnalyzer } from "./analysis";
|
||||
import { Segment, FileData } from "./workertypes";
|
||||
import { disassemble6502 } from "./cpu/disasm6502";
|
||||
|
@ -1034,7 +1034,7 @@ export function lookupSymbol(platform:Platform, addr:number, extra:boolean) {
|
|||
|
||||
/// new Machine platform adapters
|
||||
|
||||
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsBIOS, AcceptsKeyInput, SavesState, SavesInputState, HasCPU, TrapCondition, CPU, HasSerialIO, SerialIOInterface } from "./devices";
|
||||
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsBIOS, AcceptsKeyInput, SavesState, SavesInputState, HasCPU, TrapCondition, CPU, HasSerialIO, SerialIOInterface, SerialEvent } from "./devices";
|
||||
import { Probeable, RasterFrameBased, AcceptsPaddleInput, SampledAudioSink, ProbeAll, NullProbe } from "./devices";
|
||||
import { SampledAudio } from "./audio";
|
||||
import { ProbeRecorder } from "./recorder";
|
||||
|
@ -1074,10 +1074,13 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
video : RasterVideo;
|
||||
audio : SampledAudio;
|
||||
poller : ControllerPoller;
|
||||
serialIOInterface : SerialIOInterface;
|
||||
serialVisualizer : SerialIOVisualizer;
|
||||
|
||||
probeRecorder : ProbeRecorder;
|
||||
startProbing;
|
||||
stopProbing;
|
||||
|
||||
|
||||
abstract newMachine() : T;
|
||||
abstract getToolForFilename(s:string) : string;
|
||||
abstract getDefaultExtension() : string;
|
||||
|
@ -1088,7 +1091,10 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
this.mainElement = mainElement;
|
||||
}
|
||||
|
||||
reset() { this.machine.reset(); }
|
||||
reset() {
|
||||
this.machine.reset();
|
||||
if (this.serialVisualizer != null) this.serialVisualizer.reset();
|
||||
}
|
||||
loadState(s) { this.machine.loadState(s); }
|
||||
saveState() { return this.machine.saveState(); }
|
||||
getSP() { return this.machine.cpu.getSP(); }
|
||||
|
@ -1143,8 +1149,12 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
m.loadBIOS(data, title);
|
||||
};
|
||||
}
|
||||
if (hasSerialIO(m) && this.serialIOInterface) {
|
||||
m.connectSerialIO(this.serialIOInterface);
|
||||
if (hasSerialIO(m)) {
|
||||
if (this.serialIOInterface == null) {
|
||||
this.serialVisualizer = new SerialIOVisualizer(this.mainElement, m);
|
||||
} else {
|
||||
m.connectSerialIO(this.serialIOInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1155,8 +1165,6 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
|
||||
loadBIOS : (title, data) => void; // only set if hasBIOS() is true
|
||||
|
||||
serialIOInterface : SerialIOInterface; // set if hasSerialIO() is true
|
||||
|
||||
pollControls() {
|
||||
this.poller && this.poller.poll();
|
||||
if (hasPaddleInput(this.machine)) {
|
||||
|
@ -1172,6 +1180,7 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
advance(novideo:boolean) {
|
||||
var steps = this.machine.advanceFrame(this.getDebugCallback());
|
||||
if (!novideo && this.video) this.video.updateFrame();
|
||||
if (!novideo && this.serialVisualizer) this.serialVisualizer.refresh();
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
@ -1477,3 +1486,50 @@ export abstract class BaseWASMMachine {
|
|||
}
|
||||
}
|
||||
|
||||
class SerialIOVisualizer {
|
||||
|
||||
textarea : HTMLTextAreaElement;
|
||||
//vlist: VirtualTextScroller;
|
||||
device: HasSerialIO;
|
||||
lastOutCount = -1;
|
||||
lastInCount = -1;
|
||||
|
||||
constructor(parentElement: HTMLElement, device: HasSerialIO) {
|
||||
this.device = device;
|
||||
this.textarea = document.createElement("textarea");
|
||||
this.textarea.classList.add('transcript');
|
||||
this.textarea.classList.add('transcript-style-2');
|
||||
this.textarea.style.display = 'none';
|
||||
parentElement.appendChild(this.textarea);
|
||||
/*
|
||||
this.vlist = new VirtualTextScroller(parentElement);
|
||||
this.vlist.create(parentElement, 1024, this.getMemoryLineAt.bind(this));
|
||||
this.vlist.maindiv.style.height = '8em';
|
||||
this.vlist.maindiv.style.overflow = 'clip';
|
||||
*/
|
||||
}
|
||||
reset() {
|
||||
this.lastOutCount = 0;
|
||||
this.lastInCount = 0;
|
||||
this.textarea.style.display = 'none';
|
||||
}
|
||||
refresh() {
|
||||
var lastop = '';
|
||||
if (this.device.serialOut.length != this.lastOutCount) {
|
||||
var s = '';
|
||||
for (var ev of this.device.serialOut) {
|
||||
if (lastop != ev.op) {
|
||||
if (s != '') s += '\n';
|
||||
if (ev.op === 'read') s += '<< ';
|
||||
else if (ev.op === 'write') s += '>> ';
|
||||
lastop = ev.op;
|
||||
}
|
||||
if (ev.value == 10) { s += '\u21b5'; lastop = ''; }
|
||||
else { s += byteToASCII(ev.value); }
|
||||
}
|
||||
this.textarea.value = s;
|
||||
this.lastOutCount = this.device.serialOut.length;
|
||||
this.textarea.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,13 @@ export interface AcceptsPaddleInput {
|
|||
|
||||
// SERIAL I/O
|
||||
|
||||
export interface SerialEvent {
|
||||
op: 'read' | 'write';
|
||||
value: number;
|
||||
nbits: number;
|
||||
}
|
||||
|
||||
// TODO: all these needed?
|
||||
export interface SerialIOInterface {
|
||||
// from machine to platform
|
||||
clearToSend() : boolean;
|
||||
|
@ -119,10 +126,13 @@ export interface SerialIOInterface {
|
|||
// implement these too
|
||||
reset() : void;
|
||||
advance(clocks: number) : void;
|
||||
// refresh() : void;
|
||||
}
|
||||
|
||||
export interface HasSerialIO {
|
||||
connectSerialIO(serial: SerialIOInterface);
|
||||
serialOut?: SerialEvent[]; // outgoing event log
|
||||
serialIn?: SerialEvent[]; // incoming queue
|
||||
}
|
||||
|
||||
/// PROFILER
|
||||
|
@ -131,13 +141,6 @@ export interface Probeable {
|
|||
connectProbe(probe: ProbeAll) : void;
|
||||
}
|
||||
|
||||
export function xorshift32(x : number) : number {
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
return x;
|
||||
}
|
||||
|
||||
export interface ProbeTime {
|
||||
logClocks(clocks:number);
|
||||
logNewScanline();
|
||||
|
|
|
@ -714,3 +714,69 @@ export function getMousePos(canvas : HTMLCanvasElement, evt) : {x:number,y:numbe
|
|||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
// TODO: https://stackoverflow.com/questions/10463518/converting-em-to-px-in-javascript-and-getting-default-font-size
|
||||
export function getVisibleEditorLineHeight() : number{
|
||||
return $("#booksMenuButton").first().height();
|
||||
}
|
||||
|
||||
declare var VirtualList;
|
||||
|
||||
export interface VirtualTextLine {
|
||||
text : string;
|
||||
clas? : string;
|
||||
}
|
||||
|
||||
export class VirtualTextScroller {
|
||||
memorylist;
|
||||
maindiv : HTMLElement;
|
||||
getLineAt : (row:number) => VirtualTextLine;
|
||||
|
||||
constructor(parent : HTMLElement) {
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute("class", "memdump");
|
||||
parent.appendChild(div);
|
||||
this.maindiv = div;
|
||||
}
|
||||
|
||||
create(workspace : HTMLElement, maxRowCount : number, fn : (row:number) => VirtualTextLine) {
|
||||
this.getLineAt = fn;
|
||||
this.memorylist = new VirtualList({
|
||||
w: $(workspace).width(),
|
||||
h: $(workspace).height(),
|
||||
itemHeight: getVisibleEditorLineHeight(),
|
||||
totalRows: maxRowCount, // TODO?
|
||||
generatorFn: (row : number) => {
|
||||
var line = fn(row);
|
||||
var linediv = document.createElement("div");
|
||||
linediv.appendChild(document.createTextNode(line.text));
|
||||
if (line.clas != null) linediv.className = line.clas;
|
||||
return linediv;
|
||||
}
|
||||
});
|
||||
$(this.maindiv).append(this.memorylist.container);
|
||||
}
|
||||
|
||||
// TODO: refactor with elsewhere
|
||||
refresh() {
|
||||
if (this.memorylist) {
|
||||
$(this.maindiv).find('[data-index]').each( (i,e) => {
|
||||
var div = e;
|
||||
var row = parseInt(div.getAttribute('data-index'));
|
||||
var oldtext = div.innerText;
|
||||
var line = this.getLineAt(row);
|
||||
var newtext = line.text;
|
||||
if (oldtext != newtext) {
|
||||
div.innerText = newtext;
|
||||
if (line.clas != null && !div.classList.contains(line.clas)) {
|
||||
var oldclasses = Array.from(div.classList);
|
||||
oldclasses.forEach((c) => div.classList.remove(c));
|
||||
div.classList.add('vrow');
|
||||
div.classList.add(line.clas);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -498,3 +498,11 @@ export function convertDataToUint8Array(data: string|Uint8Array) : Uint8Array {
|
|||
export function convertDataToString(data: string|Uint8Array) : string {
|
||||
return (data instanceof Uint8Array) ? byteArrayToUTF8(data) : data;
|
||||
}
|
||||
|
||||
export function byteToASCII(b: number) : string {
|
||||
if (b < 32)
|
||||
return String.fromCharCode(b + 0x2400);
|
||||
else
|
||||
return String.fromCharCode(b);
|
||||
}
|
||||
|
||||
|
|
|
@ -2341,7 +2341,7 @@ export function getSaveState() {
|
|||
export function emulationHalted(err: EmuHalt) {
|
||||
var msg = (err && err.message) || msg;
|
||||
showExceptionAsError(err, msg);
|
||||
projectWindows.refresh(true);
|
||||
projectWindows.refresh(false); // don't mess with cursor
|
||||
if (platform.saveState) showDebugInfo(platform.saveState());
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ import { hex, lpad, rpad, safeident, rgb2bgr } from "../common/util";
|
|||
import { CodeAnalyzer } from "../common/analysis";
|
||||
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows, runToPC, qs } from "./ui";
|
||||
import { ProbeRecorder, ProbeFlags } from "../common/recorder";
|
||||
import { getMousePos, dumpRAM } from "../common/emu";
|
||||
import { getMousePos, dumpRAM, getVisibleEditorLineHeight, VirtualTextScroller, VirtualTextLine } from "../common/emu";
|
||||
import * as pixed from "./pixeleditor";
|
||||
|
||||
declare var Mousetrap;
|
||||
|
||||
export interface ProjectView {
|
||||
|
@ -49,11 +50,6 @@ function createTextSpan(text:string, className:string) : HTMLElement {
|
|||
return span;
|
||||
}
|
||||
|
||||
// TODO: https://stackoverflow.com/questions/10463518/converting-em-to-px-in-javascript-and-getting-default-font-size
|
||||
function getVisibleEditorLineHeight() : number{
|
||||
return $("#booksMenuButton").first().height();
|
||||
}
|
||||
|
||||
function newDiv(parent?, cls? : string) {
|
||||
var div = $(document.createElement("div"));
|
||||
if (parent) div.appendTo(parent)
|
||||
|
@ -620,66 +616,6 @@ export class ListingView extends DisassemblerView implements ProjectView {
|
|||
|
||||
///
|
||||
|
||||
interface VirtualTextLine {
|
||||
text : string;
|
||||
clas? : string;
|
||||
}
|
||||
|
||||
class VirtualTextScroller {
|
||||
memorylist;
|
||||
maindiv : HTMLElement;
|
||||
getLineAt : (row:number) => VirtualTextLine;
|
||||
|
||||
constructor(parent : HTMLElement) {
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute("class", "memdump");
|
||||
parent.appendChild(div);
|
||||
this.maindiv = div;
|
||||
}
|
||||
|
||||
create(workspace : HTMLElement, maxRowCount : number, fn : (row:number) => VirtualTextLine) {
|
||||
this.getLineAt = fn;
|
||||
this.memorylist = new VirtualList({
|
||||
w: $(workspace).width(),
|
||||
h: $(workspace).height(),
|
||||
itemHeight: getVisibleEditorLineHeight(),
|
||||
totalRows: maxRowCount, // TODO?
|
||||
generatorFn: (row : number) => {
|
||||
var line = fn(row);
|
||||
var linediv = document.createElement("div");
|
||||
linediv.appendChild(document.createTextNode(line.text));
|
||||
if (line.clas != null) linediv.className = line.clas;
|
||||
return linediv;
|
||||
}
|
||||
});
|
||||
$(this.maindiv).append(this.memorylist.container);
|
||||
}
|
||||
|
||||
// TODO: refactor with elsewhere
|
||||
refresh() {
|
||||
if (this.memorylist) {
|
||||
$(this.maindiv).find('[data-index]').each( (i,e) => {
|
||||
var div = e;
|
||||
var row = parseInt(div.getAttribute('data-index'));
|
||||
var oldtext = div.innerText;
|
||||
var line = this.getLineAt(row);
|
||||
var newtext = line.text;
|
||||
if (oldtext != newtext) {
|
||||
div.innerText = newtext;
|
||||
if (line.clas != null && !div.classList.contains(line.clas)) {
|
||||
var oldclasses = Array.from(div.classList);
|
||||
oldclasses.forEach((c) => div.classList.remove(c));
|
||||
div.classList.add('vrow');
|
||||
div.classList.add(line.clas);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
function ignoreSymbol(sym:string) {
|
||||
return sym.endsWith('_SIZE__') || sym.endsWith('_LAST__') || sym.endsWith('STACKSIZE__') || sym.endsWith('FILEOFFS__')
|
||||
|| sym.startsWith('l__') || sym.startsWith('s__') || sym.startsWith('.__.');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
import { MOS6502, MOS6502State } from "../common/cpu/MOS6502";
|
||||
import { Bus, BasicScanlineMachine, xorshift32, SavesState } from "../common/devices";
|
||||
import { Bus, BasicScanlineMachine, SavesState } from "../common/devices";
|
||||
import { KeyFlags } from "../common/emu"; // TODO
|
||||
import { hex, lzgmini, stringToByteArray, RGBA, printFlags } from "../common/util";
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
import { ARM32CPU, ARMCoreState } from "../common/cpu/ARM";
|
||||
import { BasicScanlineMachine } from "../common/devices";
|
||||
import { KeyFlags, newAddressDecoder, padBytes, Keys, makeKeycodeMap, newKeyboardHandler } from "../common/emu";
|
||||
import { TssChannelAdapter, MasterAudio, AY38910_Audio } from "../common/audio";
|
||||
import { BasicScanlineMachine, HasSerialIO, SerialEvent, SerialIOInterface } from "../common/devices";
|
||||
import { newAddressDecoder, Keys, makeKeycodeMap, newKeyboardHandler } from "../common/emu";
|
||||
import { Debuggable, EmuState } from "../common/baseplatform";
|
||||
import { hex, lpad, printFlags } from "../common/util";
|
||||
import { hex, lpad } from "../common/util";
|
||||
|
||||
var GBA_KEYCODE_MAP = makeKeycodeMap([
|
||||
[Keys.A, 0, 0x1],
|
||||
|
@ -23,10 +22,11 @@ const RAM_START = 0x2000000;
|
|||
const RAM_SIZE = 0x80000;
|
||||
const IO_START = 0x4000000;
|
||||
const IO_SIZE = 0x100;
|
||||
const MAX_SERIAL_CHARS = 1000000;
|
||||
|
||||
const CPU_FREQ = 4000000; // 4 MHz
|
||||
|
||||
export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
||||
export class ARM32Machine extends BasicScanlineMachine implements Debuggable, HasSerialIO {
|
||||
|
||||
cpuFrequency = CPU_FREQ; // MHz
|
||||
canvasWidth = 160;
|
||||
|
@ -43,6 +43,9 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
pixels8 : Uint8Array;
|
||||
vidbase : number = 0;
|
||||
brightness : number = 255;
|
||||
serial : SerialIOInterface;
|
||||
serialOut : SerialEvent[];
|
||||
serialIn : SerialEvent[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -56,6 +59,16 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
this.pixels8 = new Uint8Array(pixels.buffer);
|
||||
}
|
||||
|
||||
connectSerialIO(serial: SerialIOInterface) {
|
||||
this.serial = serial;
|
||||
}
|
||||
|
||||
reset() {
|
||||
super.reset();
|
||||
this.serialOut = [];
|
||||
this.serialIn = [];
|
||||
}
|
||||
|
||||
// TODO: 32-bit bus?
|
||||
|
||||
read = newAddressDecoder([
|
||||
|
@ -83,6 +96,15 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
switch (a) {
|
||||
case 0x0:
|
||||
return this.inputs[0];
|
||||
case 0x40:
|
||||
return (this.serial.byteAvailable() ? 0x80 : 0) | (this.serial.clearToSend() ? 0x40 : 0);
|
||||
case 0x44:
|
||||
let evin = this.serialIn.shift();
|
||||
if (evin != null) {
|
||||
this.serialOut.push(evin);
|
||||
return evin.value;
|
||||
} else
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -93,6 +115,11 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
case 0x0:
|
||||
//this.brightness = v & 0xff;
|
||||
break;
|
||||
case 0x48:
|
||||
if (this.serialOut.length < MAX_SERIAL_CHARS) {
|
||||
this.serialOut.push({op:'write', value:v, nbits:8});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +142,7 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
}
|
||||
|
||||
getDebugCategories() {
|
||||
return ['CPU', 'Stack'];
|
||||
return ['CPU'];
|
||||
}
|
||||
|
||||
getDebugInfo?(category: string, state: EmuState) : string {
|
||||
|
@ -142,6 +169,20 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
|
|||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
saveState() {
|
||||
var state = super.saveState() as any;
|
||||
state.serial = {
|
||||
sin: this.serialIn.slice(0),
|
||||
sout : this.serialOut.slice(0)
|
||||
}
|
||||
return state;
|
||||
}
|
||||
loadState(state) {
|
||||
super.loadState(state);
|
||||
this.serialIn = state.serial.sin;
|
||||
this.serialOut = state.serial.sout;
|
||||
}
|
||||
}
|
||||
|
||||
const MODE_NAMES = {
|
||||
|
|
|
@ -253,6 +253,7 @@ class ARM32Platform extends BaseARMMachinePlatform<ARM32Machine> implements Plat
|
|||
getMemoryMap = function() { return { main:[
|
||||
{name:'ROM',start:0x0000000,size:0x80000,type:'rom'},
|
||||
{name:'RAM',start:0x2000000,size:0x80000,type:'ram'},
|
||||
{name:'I/O',start:0x4000000,size:0x100,type:'io'},
|
||||
] } };
|
||||
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
|
||||
var is_thumb = this.machine.cpu.isThumb();
|
||||
|
|
Loading…
Reference in New Issue