arm: added serial console, SerialIOInterface needs refactoring; moved VirtualTextScroller

This commit is contained in:
Steven Hugg 2021-06-14 14:38:51 -05:00
parent 240578d942
commit a7f62079db
9 changed files with 200 additions and 89 deletions

View File

@ -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';
}
}
}

View File

@ -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();

View File

@ -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);
}
}
});
}
}
}

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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('.__.');

View File

@ -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";

View File

@ -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 = {

View File

@ -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();