added base_z80 platform and viz.html example

This commit is contained in:
Steven Hugg 2017-04-29 11:31:11 -04:00
parent 1f45dfdd74
commit 5ae8d28922
14 changed files with 496 additions and 244 deletions

11
bootstrap/css/bootstrap-darkly.min.css vendored Normal file

File diff suppressed because one or more lines are too long

204
css/ui.css Normal file
View File

@ -0,0 +1,204 @@
.dbg_info {
font-size: 0.8em;
}
.gutter-offset {
width: 3em;
}
.gutter-bytes {
width: 6em;
}
.gutter-clock {
width: 0.5em;
}
.gutter-info {
width: 1em;
}
.tooltipbox {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltipbox .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
/* Position the tooltip */
position: absolute;
z-index: 10;
}
.tooltipbox:hover .tooltiptext {
visibility: visible;
}
#controls_top {
position: absolute;
padding: 0.5em;
height:3em;
width:100%;
background-color:#999;
}
#notebook {
position:absolute;
top:3em;
bottom:0;
left:0;
right:0;
background-color: #666;
overflow: hidden;;
}
div.workspace {
background-color:#999;
}
div.editor {
width:50%;
line-height:1.25;
font-size:12pt;
}
div.emulator {
position:absolute;
left:50%;
top:0;
width:50%;
background-color: #666;
margin-top: 20px auto 0;
}
div.debugwindow {
position:absolute;
left:50%;
top:0;
width:50%;
background-color: #666;
color: #66ff66;
white-space: pre;
margin-top: 20px auto 0;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 10pt;
}
div.mem_info {
position: fixed;
left: 51%;
bottom: 10px;
background-color: #333;
color: #66ff66;
white-space: pre;
padding: 20px;
z-index: 12;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 12pt;
}
.btn_group {
border-radius:6px;
padding:6px;
margin-left:8px;
background-color: #666;
}
.btn_group.debug_group {
}
.btn_group.view_group {
}
.seg_code { color: #ff9966; }
.seg_data { color: #66ff66; }
.seg_stack { color: #ffff66; }
.seg_unknown { color: #cccccc; }
span.hilite {
color: #ff66ff;
}
div.has-errors {
background-color: #ff6666 !important;
}
div.is-busy-unused {
background-color: #8888bb !important;
}
div.menu_div {
position: absolute;
width: 200px;
}
div.booklink {
position: fixed;
bottom: 0;
right: 0;
padding: 6px;
background-color: #ffffff;
display:none;
}
div.twitbtn {
position: fixed;
bottom: 0;
left: 50%;
padding: 6px;
}
a {
color:#333399;
font-weight: bold;
}
a.dropdown-toggle {
color:#66ee66;
padding:3px;
}
// http://stackoverflow.com/questions/18023493/bootstrap-3-dropdown-sub-menu-missing
.dropdown-submenu {
position:relative;
}
.dropdown-submenu>.dropdown-menu {
top:0;
left:100%;
margin-top:-6px;
margin-left:-1px;
-webkit-border-radius:0 6px 6px 6px;
-moz-border-radius:0 6px 6px 6px;
border-radius:0 6px 6px 6px;
}
.dropdown-submenu:hover>.dropdown-menu {
display:block;
}
.dropdown-submenu>a:after {
display:block;
content:" ";
float:right;
width:0;
height:0;
border-color:transparent;
border-style:solid;
border-width:5px 0 5px 5px;
border-left-color:#cccccc;
margin-top:5px;
margin-right:-10px;
}
.dropdown-submenu:hover>a:after {
border-left-color:#ffffff;
}
.dropdown-submenu.pull-left {
float:none;
}
.dropdown-submenu.pull-left>.dropdown-menu {
left:-100%;
margin-left:10px;
-webkit-border-radius:6px 0 6px 6px;
-moz-border-radius:6px 0 6px 6px;
border-radius:6px 0 6px 6px;
}
.emubevel {
padding:4%;
background:#333;
}
.emuvideo {
border-radius:20px;
border: 4px solid #222;
padding: 30px;
margin-top: 20px;
margin-bottom: 20px;
background: #000;
outline-color: #666;
}
canvas.pixelated {
image-rendering: optimizeSpeed; /* Older versions of FF */
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
image-rendering: -webkit-optimize-contrast; /* Safari */
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
image-rendering: pixelated; /* Awesome future-browsers */
-ms-interpolation-mode: nearest-neighbor; /* IE */
}

View File

@ -7,211 +7,8 @@ body {
overflow: hidden !important;
font-size: 11px;
}
.dbg_info {
font-size: 0.8em;
}
.gutter-offset {
width: 3em;
}
.gutter-bytes {
width: 6em;
}
.gutter-clock {
width: 0.5em;
}
.gutter-info {
width: 1em;
}
.tooltipbox {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltipbox .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
/* Position the tooltip */
position: absolute;
z-index: 10;
}
.tooltipbox:hover .tooltiptext {
visibility: visible;
}
#controls_top {
position: absolute;
padding: 0.5em;
height:3em;
width:100%;
background-color:#999;
}
#notebook {
position:absolute;
top:3em;
bottom:0;
left:0;
right:0;
background-color: #666;
overflow: hidden;;
}
div.workspace {
background-color:#999;
}
div.editor {
width:50%;
line-height:1.25;
font-size:12pt;
}
div.emulator {
position:absolute;
left:50%;
top:0;
width:50%;
background-color: #666;
margin-top: 20px auto 0;
}
div.debugwindow {
position:absolute;
left:50%;
top:0;
width:50%;
background-color: #666;
color: #66ff66;
white-space: pre;
margin-top: 20px auto 0;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 10pt;
}
div.mem_info {
position: fixed;
left: 51%;
bottom: 10px;
background-color: #333;
color: #66ff66;
white-space: pre;
padding: 20px;
z-index: 12;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 12pt;
}
.btn_group {
border-radius:6px;
padding:6px;
margin-left:8px;
background-color: #666;
}
.btn_group.debug_group {
}
.btn_group.view_group {
}
.seg_code { color: #ff9966; }
.seg_data { color: #66ff66; }
.seg_stack { color: #ffff66; }
.seg_unknown { color: #cccccc; }
span.hilite {
color: #ff66ff;
}
div.has-errors {
background-color: #ff6666 !important;
}
div.is-busy-unused {
background-color: #8888bb !important;
}
div.menu_div {
position: absolute;
width: 200px;
}
div.booklink {
position: fixed;
bottom: 0;
right: 0;
padding: 6px;
background-color: #ffffff;
display:none;
}
div.twitbtn {
position: fixed;
bottom: 0;
left: 50%;
padding: 6px;
}
a {
color:#333399;
font-weight: bold;
}
a.dropdown-toggle {
color:#66ee66;
padding:3px;
}
// http://stackoverflow.com/questions/18023493/bootstrap-3-dropdown-sub-menu-missing
.dropdown-submenu {
position:relative;
}
.dropdown-submenu>.dropdown-menu {
top:0;
left:100%;
margin-top:-6px;
margin-left:-1px;
-webkit-border-radius:0 6px 6px 6px;
-moz-border-radius:0 6px 6px 6px;
border-radius:0 6px 6px 6px;
}
.dropdown-submenu:hover>.dropdown-menu {
display:block;
}
.dropdown-submenu>a:after {
display:block;
content:" ";
float:right;
width:0;
height:0;
border-color:transparent;
border-style:solid;
border-width:5px 0 5px 5px;
border-left-color:#cccccc;
margin-top:5px;
margin-right:-10px;
}
.dropdown-submenu:hover>a:after {
border-left-color:#ffffff;
}
.dropdown-submenu.pull-left {
float:none;
}
.dropdown-submenu.pull-left>.dropdown-menu {
left:-100%;
margin-left:10px;
-webkit-border-radius:6px 0 6px 6px;
-moz-border-radius:6px 0 6px 6px;
border-radius:6px 0 6px 6px;
}
.emubevel {
padding:4%;
background:#333;
}
.emuvideo {
border-radius:20px;
border: 4px solid #222;
padding: 30px;
margin-top: 20px;
margin-bottom: 20px;
background: #000;
outline-color: #666;
}
canvas.pixelated {
image-rendering: optimizeSpeed; /* Older versions of FF */
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
image-rendering: -webkit-optimize-contrast; /* Safari */
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
image-rendering: pixelated; /* Awesome future-browsers */
-ms-interpolation-mode: nearest-neighbor; /* IE */
}
</style>
<link rel="stylesheet" href="css/ui.css">
</head>
<body>

3
presets/base_z80/empty.c Normal file
View File

@ -0,0 +1,3 @@
void func() {
}

9
presets/base_z80/fib.c Normal file
View File

@ -0,0 +1,9 @@
int Fibonacci(int n)
{
if ( n == 0 )
return 0;
else if ( n == 1 )
return 1;
else
return ( Fibonacci(n-1) + Fibonacci(n-2) );
}

31
presets/base_z80/gfx.c Normal file
View File

@ -0,0 +1,31 @@
#include <string.h>
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
byte __at (0x2400) vidmem[224][32]; // 256x224x1 video memory
void clrscr() {
memset(vidmem, 0, sizeof(vidmem));
}
void xor_pixel(byte x, byte y) {
byte* dest = &vidmem[x][y>>3];
*dest ^= 0x1 << (y&7);
}
byte xor_sprite(const byte* src, byte x, byte y) {
byte i,j;
byte result = 0;
byte* dest = &vidmem[x][y];
byte w = *src++;
byte h = *src++;
for (j=0; j<h; j++) {
for (i=0; i<w; i++) {
result |= (*dest++ ^= *src++);
}
dest += 32-w;
}
return result;
}

8
presets/base_z80/prng.c Normal file
View File

@ -0,0 +1,8 @@
unsigned int lfsr = 1;
unsigned int rand() {
char lsb = lfsr & 1;
lfsr >>= 1;
if (lsb) lfsr ^= 0xd400;
return lfsr;
}

View File

@ -0,0 +1,3 @@
int func(int x) {
return x * 2;
}

View File

@ -0,0 +1,3 @@
int func(int x) {
return x / 4;
}

View File

@ -0,0 +1,3 @@
void func() {
}

79
src/platform/base_z80.js Normal file
View File

@ -0,0 +1,79 @@
"use strict";
var BASEZ80_PRESETS = [
{id:'simple1.c', name:'Multiply by 2'},
{id:'simple2.c', name:'Divide by 4'},
{id:'prng.c', name:'Pseudorandom Numbers'},
{id:'fib.c', name:'Fibonacci'},
{id:'gfx.c', name:'Graphics'},
{id:'empty.c', name:'Your Code Here...'},
];
var Base_Z80Platform = function(mainElement) {
var self = this;
this.__proto__ = new BaseZ80Platform();
var cpu, ram, membus, iobus, rom, timer;
this.getPresets = function() {
return BASEZ80_PRESETS;
}
this.start = function() {
ram = new RAM(0x8000);
membus = {
read: new AddressDecoder([
[0x0000, 0x7fff, 0x7fff, function(a) { return rom ? rom[a] : null; }],
[0x8000, 0xffff, 0x7fff, function(a) { return ram.mem[a]; }],
]),
write: new AddressDecoder([
[0x8000, 0xffff, 0x7fff, function(a,v) { ram.mem[a] = v; }],
]),
isContended: function() { return false; },
};
this.readAddress = membus.read;
iobus = {
read: function(addr) {
return 0;
},
write: function(addr, val) {
}
};
cpu = this.newCPU(membus, iobus);
}
this.loadROM = function(title, data) {
rom = padBytes(data, 0x8000);
self.reset();
}
this.loadState = function(state) {
cpu.loadState(state.c);
ram.mem.set(state.b);
}
this.saveState = function() {
return {
c:self.getCPUState(),
b:ram.mem.slice(0),
};
}
this.getCPUState = function() {
return cpu.saveState();
}
this.isRunning = function() {
return timer && timer.isRunning();
}
this.pause = function() {
if (timer) timer.stop();
}
this.resume = function() {
if (timer) timer.start();
}
this.reset = function() {
cpu.reset();
if (!this.getDebugCallback()) cpu.setTstates(0); // TODO?
}
}
PLATFORMS['base_z80'] = Base_Z80Platform;

View File

@ -111,6 +111,7 @@ var TOOL_TO_SOURCE_STYLE = {
}
var worker = new Worker("./src/worker/workermain.js");
var main_editor;
var current_output;
var current_preset_index = -1;
var current_preset_id;
@ -173,8 +174,10 @@ function getCurrentPresetTitle() {
}
function setLastPreset(id) {
localStorage.setItem("__lastplatform", platform_id);
localStorage.setItem("__lastid_"+platform_id, id);
if (platform_id != 'base_z80') { // TODO
localStorage.setItem("__lastplatform", platform_id);
localStorage.setItem("__lastid_"+platform_id, id);
}
}
function updatePreset(current_preset_id, text) {
@ -185,7 +188,7 @@ function updatePreset(current_preset_id, text) {
function loadCode(text, fileid) {
var tool = platform.getToolForFilename(fileid);
newEditor(tool && TOOL_TO_SOURCE_STYLE[tool]);
main_editor = newEditor(tool && TOOL_TO_SOURCE_STYLE[tool]);
editor.setValue(text); // calls setCode()
editor.clearHistory();
current_output = null;
@ -314,7 +317,7 @@ function _downloadROMImage(e) {
}
function populateExamples(sel) {
sel.append($("<option />").text("--------- Chapters ---------").attr('disabled',true));
sel.append($("<option />").text("--------- Examples ---------").attr('disabled',true));
for (var i=0; i<PRESETS.length; i++) {
var preset = PRESETS[i];
var name = preset.chapter ? (preset.chapter + ". " + preset.name) : preset.name;
@ -341,8 +344,10 @@ function populateFiles(sel, name, prefix) {
function updateSelector() {
var sel = $("#preset_select").empty();
populateFiles(sel, "Local Files", "local/");
populateFiles(sel, "Shared", "shared/");
if (platform_id != 'base_z80') { // TODO
populateFiles(sel, "Local Files", "local/");
populateFiles(sel, "Shared", "shared/");
}
populateExamples(sel);
// set click handlers
sel.off('change').change(function(e) {
@ -464,6 +469,7 @@ function setCompileOutput(data) {
}
}
}
updateDisassembly();
if (trace_pending_at_pc) {
showLoopTimingForPC(trace_pending_at_pc);
}
@ -664,7 +670,12 @@ function updateDisassembly() {
var state = lastDebugState || platform.saveState();
var pc = state.c.PC;
if (assemblyfile && assemblyfile.text) {
disasmview.setValue(assemblyfile.text);
var asmtext = assemblyfile.text;
if (platform_id == 'base_z80') { // TODO
asmtext = asmtext.replace(/[ ]+\d+\s+;.+\n/g, '');
asmtext = asmtext.replace(/[ ]+\d+\s+.area .+\n/g, '');
}
disasmview.setValue(asmtext);
var findPC = platform.getDebugCallback() ? pc : getCurrentPC();
if (findPC) {
var lineno = assemblyfile.findLineForOffset(findPC);
@ -673,43 +684,44 @@ function updateDisassembly() {
jumpToLine(disasmview, lineno-1);
}
}
return;
}
var curline = 0;
var selline = 0;
// TODO: not perfect disassembler
function disassemble(start, end) {
if (start < 0) start = 0;
if (end > 0xffff) end = 0xffff;
// TODO: use pc2visits
var a = start;
var s = "";
while (a < end) {
var disasm = platform.disassemble(a, platform.readAddress);
var srclinenum = sourcefile.offset2line[a];
if (srclinenum) {
var srcline = editor.getLine(srclinenum-1);
if (srcline && srcline.trim().length) {
s += "; " + srclinenum + ":\t" + srcline + "\n";
else if (platform.disassemble) {
var curline = 0;
var selline = 0;
// TODO: not perfect disassembler
function disassemble(start, end) {
if (start < 0) start = 0;
if (end > 0xffff) end = 0xffff;
// TODO: use pc2visits
var a = start;
var s = "";
while (a < end) {
var disasm = platform.disassemble(a, platform.readAddress);
var srclinenum = sourcefile.offset2line[a];
if (srclinenum) {
var srcline = editor.getLine(srclinenum-1);
if (srcline && srcline.trim().length) {
s += "; " + srclinenum + ":\t" + srcline + "\n";
}
}
var bytes = "";
for (var i=0; i<disasm.nbytes; i++)
bytes += hex(platform.readAddress(a+i));
while (bytes.length < 14)
bytes += ' ';
var dline = hex(parseInt(a)) + "\t" + bytes + "\t" + disasm.line + "\n";
s += dline;
if (a == pc) selline = curline;
curline++;
a += disasm.nbytes || 1;
}
var bytes = "";
for (var i=0; i<disasm.nbytes; i++)
bytes += hex(platform.readAddress(a+i));
while (bytes.length < 14)
bytes += ' ';
var dline = hex(parseInt(a)) + "\t" + bytes + "\t" + disasm.line + "\n";
s += dline;
if (a == pc) selline = curline;
curline++;
a += disasm.nbytes || 1;
return s;
}
return s;
var text = disassemble(pc-96, pc) + disassemble(pc, pc+96);
disasmview.setValue(text);
disasmview.setCursor(selline, 0);
jumpToLine(disasmview, selline);
}
var text = disassemble(pc-96, pc) + disassemble(pc, pc+96);
disasmview.setValue(text);
disasmview.setCursor(selline, 0);
jumpToLine(disasmview, selline);
}
}
@ -1085,6 +1097,7 @@ function showWelcomeMessage() {
tour.init();
setTimeout(function() { tour.start(); }, 2000);
}
if (qs['redir']) delete qs['redir'];
}
///////////////////////////////////////////////////
@ -1125,7 +1138,6 @@ function startPlatform() {
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
PRESETS = platform.getPresets();
if (qs['file']) {
showWelcomeMessage();
// start platform and load file
preloadWorker(qs['file']);
platform.start();
@ -1192,9 +1204,11 @@ function startUI(loadplatform) {
$.getScript(scriptfn, function() {
console.log("loaded platform", platform_id);
startPlatform();
showWelcomeMessage();
});
} else {
startPlatform();
showWelcomeMessage();
}
}
}

View File

@ -50,6 +50,13 @@ var PLATFORM_PARAMS = {
data_size: 0x400,
stack_end: 0x8000,
},
'base_z80': {
code_start: 0x0,
code_size: 0x8000,
data_start: 0x8000,
data_size: 0x8000,
stack_end: 0x0,
},
};
var loaded = {}

80
viz.html Normal file
View File

@ -0,0 +1,80 @@
<html>
<head>
<title>8bitworkshop ~ Z80 Compiler Visualization</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/ui.css">
<style type="text/css" media="screen">
div.CodeMirror {
line-height:1.25;
font-size:12pt;
border: 1px solid #eee;
height: 600pt;
}
#preset_select {
color: #000;
}
</style>
<script src="jquery/jquery-2.2.3.min.js"></script>
<link rel="stylesheet" href="bootstrap/css/bootstrap-darkly.min.css">
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="codemirror/lib/codemirror.js"></script>
<script src="codemirror/mode/clike/clike.js"></script>
<script src="codemirror/mode/6502/6502.js"></script>
<script src="codemirror/mode/z80/z80.js"></script>
<link rel="stylesheet" href="css/codemirror.css">
<script src="codemirror/addon/edit/matchbrackets.js"></script>
<script src="codemirror/addon/selection/active-line.js"></script>
</head>
<div class="container-fluid">
<div class="page-header">
<p class="lead">
Edit a C source file, and see the Z80 assembly output update in real-time.
</p>
<h6>Powered by <a target="_new" href="https://twitter.com/8bitworkshop">@8bitworkshop</a> and the Small Device C Compiler.</h6>
<select id="preset_select" name="">
</select>
</div>
<div class="row">
<div class="col-md-6">
<div id="editor" class="editor2">
</div>
</div>
<div class="col-md-6">
<div id="disassembly">
</div>
</div>
</div>
</div>
<script>
Javatari = {};
PLATFORMS = {};
</script>
<script src="src/cpu/z80.js"></script>
<script src="src/util.js"></script>
<script src="src/emu.js"></script>
<script src="src/ui.js"></script>
<script src="src/platform/base_z80.js"></script>
<script>
platform_id = 'base_z80';
startPlatform();
$('#disassembly').show();
</script>
</body>
</html>