x86: started using v86, freedos, fatfs, yasm, SmallerC

This commit is contained in:
Steven Hugg 2020-06-13 20:28:58 -05:00
parent 9396a2ddbb
commit 0d77912ccc
26 changed files with 15685 additions and 10 deletions

3
.gitignore vendored
View File

@ -6,4 +6,5 @@ local
./test/output
tests_output/
.DS_Store
tmp
tmp/
web/

View File

@ -558,3 +558,11 @@ div.asset_toolbar {
.book-title {
font-size:12pt;
}
.pc-console {
background-color: #000;
color: #7f7f7f;
white-space: pre;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 10pt;
line-height: 1.2;
}

View File

@ -516,6 +516,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<script src="codemirror/mode/verilog/verilog.js"></script>
<script src="codemirror/mode/markdown/markdown.js"></script>
<script src="codemirror/mode/javascript/javascript.js"></script>
<script src="codemirror/mode/gas/gas.js"></script>
<script src="src/codemirror/6502.js"></script>
<script src="src/codemirror/bataribasic.js"></script>
<link rel="stylesheet" href="css/codemirror.css">

8275
lib/fatfs.js Normal file

File diff suppressed because it is too large Load Diff

1281
lib/libv86.js Normal file

File diff suppressed because it is too large Load Diff

13
presets/x86/hello.asm Normal file
View File

@ -0,0 +1,13 @@
; http://www.ablmcc.edu.hk/~scy/CIT/8086_bios_and_dos_interrupts.htm
org 100h ; EXE files start at 0x100
section .text ; code section
mov dx,msg ; DX = string address
mov ah,9 ; 9 = "draw string"
int 21h
mov ah,4Ch ; 4ch = "exit to OS"
int 21h
section .data ; data section
msg db 'Hello, World!',0Dh,0Ah,'$'

152
presets/x86/mandelg.asm Normal file
View File

@ -0,0 +1,152 @@
;; Draw a grayscale mandelbrot fractal
;;
;; i compiled with yasm: yasm -o mandel.com mandel.asm
;; but i think it should be quite portable to other assemblers.
;;
;; from: https://www.reddit.com/r/asm/comments/8o8c0b/mandelbrot_x86_asm_w_vga/
SECTION .data
maxi: equ 128 ; upto maxi iterations per pixel
palscale: equ 1 ; ammount to shift to fit maxi onto palette
crestep: equ 2 ; x (mandelwidth/320) * 2^shift
cimstep: equ 3 ; y (mandelheight/200) * 2^shift
one: equ 256 ; fixed point 1.0
n15: equ -450 ; start of left side
two: equ 512 ; fixed point 2.0
four: equ 1024 ; fixed point 4.0
cim: dw 300 ; imaginary part of Y
cre: dw 0 ; imaginary part of X
x: dw 0 ; real part of X
y: dw 0 ; real part of Y
xx: dw 0 ; x*x placeholder
yy: dw 0 ; y*y placeholder
twox: dw 0 ; 2*x placeholder
xnew: dw 0 ; temporary x placeholder
row: dw 0 ; current row
col dw 0 ; current col
SECTION .text
ORG 0x100 ; dos .com file starts at 0100h
call setup ; setup screen mode
call draw ; draw the fractal
call waitkey ; wait for a keypress
exit:
mov ax, 3 ; back to text
int 0x10 ; mode
mov ax, 0x4c00 ; clean dos
int 0x21 ; exit
setup:
mov ax, 0x13 ; setup screen mode 13
int 0x10 ; 320x200x256
mov ax, 0 ; setup grayscale palette
mov dx, 0x03c8 ; setup register to receive
out dx, al ; full palette (768bytes of rgb data)
inc dx ; the next register expects data
setpalette:
out dx, al ; red value
out dx, al ; green value
out dx, al ; blue value
inc al ; next 'color'
jnz setpalette
mov ax, 0xa000 ; point es:di to
mov es, ax ; 0a000:0000
xor di, di ; so stos functions write to vram
cld ; we move forward in memory
xor ax,ax ; clear
mov cx,32000 ; screen
rep stosw
xor di, di ; restore di to start of vram
ret
waitkey:
mov ax, 0 ; wait for
int 0x16 ; keypress
jnz waitkey ; none received, start over
ret
draw:
mov word [row], 200 ; 200 rows
yloop:
mov ax, n15
mov [cre], ax ; start of left side
mov word [col], 320 ; 320 columns
xloop:
xor ax, ax
xor cx, cx ; iter = 0
mov [x], ax ; x = 0
mov [y], ax ; y = 0
whileloop: ; while ( iter < maxi && x*x + y*y < 4)
cmp cx, maxi ; if iter == maxi
je escape ; escape
mov ax, [x] ; x*x
mov bx, ax
imul bx
mov bx, one
idiv bx
mov [xx], ax
mov ax, [y] ; y*y
mov bx, ax
imul bx
mov bx, one
idiv bx
mov [yy], ax
add ax, [xx] ; if x*x + y*y ==
cmp ax, four ; four
jge escape ; escape
mov ax, [xx] ; xnew = x*x - y*y + cre
sub ax, [yy]
add ax, [cre]
mov [xnew], ax
mov ax, [x] ; x * 2 * y
mov bx, two
imul bx
mov bx, one
idiv bx
mov bx, [y]
imul bx
mov bx, one
idiv bx
add ax, [cim] ; + cim
mov [y], ax ; y = x * 2 * y + cim
mov ax, [xnew] ; x = xnew
mov [x], ax
inc cx ; ++iter
jmp whileloop
escape:
mov al, cl ; copy color index (iter)
cmp al, maxi ; plot pixel
jne color ; w/ black if maximum reached
xor al, al
color: ; otherwise w/ palette value
shl al, palscale ; scale to fit palette
stosb ; write pixel
add word [cre], crestep ; cre += crestep
dec word [col]
jnz xloop ; next column
sub word [cim], cimstep ; cim -= cimstep
dec word [row] ; next row
jnz yloop
ret

11
presets/x86/skeleton.yasm Normal file
View File

@ -0,0 +1,11 @@
section .text
org 100h
mov dx,msg ; DX = string address
mov ah,9 ; 9 = "draw string"
int 21h
mov ah,4Ch ; 4ch = "exit to OS"
int 21h
section .data
msg db 'Hello, World!',0Dh,0Ah,'$'

418
presets/x86/snake.c Normal file
View File

@ -0,0 +1,418 @@
/*
Compile:
smlrc -seg16 -nobss snake.c snake.asm
nasm -f bin snake.asm -o snake.com
Run snake.com in DOS, 32-bit Windows or DosBox.
*/
asm("org 0x0");
int main(void);
void start(void)
{
asm("mov ax,cs");
asm("mov ds,ax");
asm("mov es,ax");
asm("mov fs,ax");
main();
asm("mov ax,4ch");
asm("int 21h");
}
// defines the game speed
#define DELAY 150
// video mode, video buffer segment and dimensions
#define VMODE 1 // 40x25 color text mode
#define VSEG 0xB800
#define VWIDTH 40
#define VHEIGHT 25
// foreground and background colors
#define FORE_BLACK 0x00
#define FORE_BLUE 0x01
#define FORE_GREEN 0x02
#define FORE_CYAN 0x03
#define FORE_RED 0x04
#define FORE_MAGENTA 0x05
#define FORE_BROWN 0x06
#define FORE_WHITE 0x07
#define FORE_GRAY 0x08
#define FORE_BRIGHT_BLUE 0x09
#define FORE_BRIGHT_GREEN 0x0A
#define FORE_BRIGHT_CYAN 0x0B
#define FORE_BRIGHT_RED 0x0C
#define FORE_BRIGHT_MAGENTA 0x0D
#define FORE_YELLOW 0x0E
#define FORE_BRIGHT_WHITE 0x0F
#define BACK_BLACK 0x00
#define BACK_BLUE 0x10
#define BACK_GREEN 0x20
#define BACK_CYAN 0x30
#define BACK_RED 0x40
#define BACK_MAGENTA 0x50
#define BACK_BROWN 0x60
#define BACK_WHITE 0x70
// key codes
#define KEY_ESCAPE 0x011B
#define KEY_ENTER 0x1C0D
#define KEY_UP 0x4800
#define KEY_LEFT 0x4B00
#define KEY_RIGHT 0x4D00
#define KEY_DOWN 0x5000
// snake state
#define MIN_LENGTH 6
#define MAX_LENGTH (VWIDTH * VHEIGHT)
unsigned snake[MAX_LENGTH][2]; // coordinates
unsigned length;
unsigned direction;
// target
unsigned target[2]; // coordinates
unsigned score;
int BiosKeyAvailable(void)
{
asm("mov ah, 1\n"
"int 0x16\n"
"setnz al\n"
"cbw");
}
unsigned BiosReadKey(void)
{
asm("mov ah, 0\n"
"int 0x16");
}
void BiosSetGfxMode(unsigned mode)
{
asm("mov ah, 0\n"
"mov al, [bp + 4]\n"
"int 0x10");
}
void pokeb(unsigned seg, unsigned ofs, unsigned char val)
{
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov al, [bp + 8]\n"
"mov [bx], al\n"
"pop ds");
}
void poke(unsigned seg, unsigned ofs, unsigned val)
{
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov ax, [bp + 8]\n"
"mov [bx], ax\n"
"pop ds");
}
unsigned char peekb(unsigned seg, unsigned ofs)
{
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov al, [bx]\n"
"mov ah, 0\n"
"pop ds");
}
unsigned peek(unsigned seg, unsigned ofs)
{
asm("push ds\n"
"mov ds, [bp + 4]\n"
"mov bx, [bp + 6]\n"
"mov ax, [bx]\n"
"pop ds");
}
void BiosGetTicks(unsigned ticks[2])
{
unsigned low, high1, high2;
high2 = peek(0x40, 0x6E);
do
{
high1 = high2;
low = peek(0x40, 0x6C);
high2 = peek(0x40, 0x6E);
// make sure the top 16 bits of the ticks counter haven't changed meanwhile
} while (high1 != high2);
ticks[0] = low;
ticks[1] = high2;
}
void delay(unsigned milliseconds)
{
unsigned tcnt = (milliseconds + 27) / 55; // the 32-bit ticks counter increments every 55 ms
unsigned ticks[2][2];
BiosGetTicks(ticks[0]);
for (;;)
{
BiosGetTicks(ticks[1]);
// ticks[1] -= ticks[0]
if (ticks[1][0] < ticks[0][0])
--ticks[1][1];
ticks[1][0] -= ticks[0][0];
ticks[1][1] -= ticks[0][1];
// ticks[1] >= tcnt ?
if (ticks[1][0] >= tcnt || ticks[1][1])
break;
}
}
void lineh(unsigned x, unsigned y, unsigned w, unsigned chr, unsigned color)
{
unsigned ofs = (y * VWIDTH + x) * 2;
unsigned v = (color << 8) | chr;
while (w--)
{
poke(VSEG, ofs, v);
ofs += 2;
}
}
void linev(unsigned x, unsigned y, unsigned h, unsigned chr, unsigned color)
{
unsigned ofs = (y * VWIDTH + x) * 2;
unsigned v = (color << 8) | chr;
while (h--)
{
poke(VSEG, ofs, v);
ofs += VWIDTH * 2;
}
}
void box(unsigned x, unsigned y, unsigned w, unsigned h, unsigned chr, unsigned color, unsigned solid)
{
if (solid)
{
while (h--)
lineh(x, y++, w, chr, color);
}
else
{
lineh(x, y, w, chr, color);
linev(x + w - 1, y, h, chr, color);
lineh(x, y + h - 1, w, chr, color);
linev(x, y, h, chr, color);
}
}
void text(unsigned x, unsigned y, char* s, unsigned color)
{
unsigned ofs = (y * VWIDTH + x) * 2;
while (*s)
{
unsigned v = (color << 8) | *s++;
poke(VSEG, ofs, v);
ofs += 2;
}
}
void number(unsigned x, unsigned y, unsigned n, unsigned color)
{
char s[6];
int i;
for (i = 4; i >= 0; --i)
{
s[i] = '0' + n % 10;
n /= 10;
}
s[5] = 0;
text(x, y, s, color);
}
#define RAND_MAX 0x7FFF
unsigned rand_next[2] = { 1, 0 };
int rand(void)
{
asm("mov eax, [_rand_next]\n"
"imul eax, eax, 1103515245\n"
"add eax, 12345\n"
"mov [_rand_next], eax\n"
"shr eax, 16\n"
"and ax, 0x7FFF\n");
}
void srand(unsigned seed)
{
rand_next[0] = seed;
}
void play(void);
int main(void)
{
unsigned ticks[2];
BiosSetGfxMode(VMODE);
text((VWIDTH - 20) / 2, VHEIGHT / 2, "Press a key to start", BACK_BLACK | FORE_BRIGHT_WHITE);
while (BiosKeyAvailable()) BiosReadKey(); // clear the keyboard buffer, just in case
BiosReadKey();
BiosGetTicks(ticks);
srand(ticks[0]);
play();
BiosSetGfxMode(3); // 80x25 color text mode
return 0;
}
void newtarget(void)
{
for (;;)
{
unsigned bit = 0, i;
i = rand() % ((VWIDTH - 2) * (VHEIGHT - 2));
target[0] = 1 + i % (VWIDTH - 2);
target[1] = 1 + i / (VWIDTH - 2);
for (i = 0; i < length; ++i)
{
if (target[0] == snake[i][0] &&
target[1] == snake[i][1])
{
bit = 1;
break;
}
}
if (!bit)
break;
}
text(target[0], target[1], "$", BACK_GREEN | FORE_YELLOW);
}
void play(void)
{
unsigned i, key;
// set up the field
box(0, 0, VWIDTH, VHEIGHT, ' ', BACK_GREEN | FORE_YELLOW, 1);
box(0, 0, VWIDTH, VHEIGHT, ' ', BACK_BROWN | FORE_BLACK, 0);
text((VWIDTH - 12) / 2, 0, "Score: ", BACK_BROWN | FORE_BLACK);
score = 0;
number((VWIDTH - 12) / 2 + 7, 0, score, BACK_BROWN | FORE_BLACK);
// set up the snake
for (length = 0; length < MIN_LENGTH; ++length)
{
snake[length][0] = VWIDTH / 2;
snake[length][1] = VHEIGHT - 1 - MIN_LENGTH + length;
text(snake[length][0], snake[length][1], "O", BACK_GREEN | FORE_YELLOW);
}
direction = KEY_UP;
// set up the target
newtarget();
for (;;)
{
key = 0;
if (BiosKeyAvailable())
key = BiosReadKey();
delay(DELAY);
// update the direction
switch (key)
{
case KEY_ESCAPE:
return;
case KEY_LEFT:
if (direction != KEY_RIGHT)
direction = key;
break;
case KEY_RIGHT:
if (direction != KEY_LEFT)
direction = key;
break;
case KEY_UP:
if (direction != KEY_DOWN)
direction = key;
break;
case KEY_DOWN:
if (direction != KEY_UP)
direction = key;
break;
}
// move the snake
text(snake[length - 1][0], snake[length - 1][1], " ", BACK_GREEN | FORE_YELLOW); // erase the tail
for (i = length - 1; i > 0; --i) // update the body position
{
snake[i][0] = snake[i - 1][0];
snake[i][1] = snake[i - 1][1];
}
switch (direction) // update the head position
{
case KEY_LEFT:
--snake[0][0];
break;
case KEY_RIGHT:
++snake[0][0];
break;
case KEY_UP:
--snake[0][1];
break;
case KEY_DOWN:
++snake[0][1];
break;
}
text(snake[0][0], snake[0][1], "O", BACK_GREEN | FORE_YELLOW); // draw the head
// check the head against the boundaries
if (snake[0][0] < 1 || snake[0][0] >= VWIDTH - 1 ||
snake[0][1] < 1 || snake[0][1] >= VHEIGHT - 1)
break;
// check for self biting
{
unsigned bit = 0;
for (i = 1; i < length; ++i)
{
if (snake[0][0] == snake[i][0] &&
snake[0][1] == snake[i][1])
{
bit = 1;
break;
}
}
if (bit)
break;
}
// check for hitting the target
if (snake[0][0] == target[0] &&
snake[0][1] == target[1])
{
// enlarge the snake
snake[length][0] = snake[length - 1][0];
snake[length][1] = snake[length - 1][1];
++length;
++score;
number((VWIDTH - 12) / 2 + 7, 0, score, BACK_BROWN | FORE_BLACK);
// set up the target
newtarget();
}
}
text((VWIDTH - 10) / 2, VHEIGHT / 2, "Game Over!", BACK_BLACK | FORE_BRIGHT_WHITE);
delay(1000);
while (BiosKeyAvailable()) BiosReadKey(); // clear the keyboard buffer
BiosReadKey();
}

BIN
res/freedos722.img Normal file

Binary file not shown.

BIN
res/seabios.bin Normal file

Binary file not shown.

BIN
res/vgabios.bin Normal file

Binary file not shown.

View File

@ -0,0 +1,165 @@
/****************************************************************************
Midway/Williams Audio Boards
----------------------------
6809 MEMORY MAP
Function Address R/W Data
---------------------------------------------------------------
Program RAM 0000-07FF R/W D0-D7
Music (YM-2151) 2000-2001 R/W D0-D7
6821 PIA 4000-4003 R/W D0-D7
HC55516 clock low, digit latch 6000 W D0
HC55516 clock high 6800 W xx
Bank select 7800 W D0-D2
Banked Program ROM 8000-FFFF R D0-D7
****************************************************************************/
import { RAM, newAddressDecoder } from "../emu";
declare function importScripts(path:string);
declare function postMessage(msg);
var cpu, ram, rom, membus, iobus;
var audio;
var command = 0;
var dac = 0;
var current_buffer;
var tstates;
var last_tstate;
var timer;
var curTime;
var timerPeriod = 20;
var sampleRate;
var numChannels = 2;
var bufferLength;
var cpuFrequency = 18432000/6; // 3.072 MHz
var cpuCyclesPerFrame = cpuFrequency/60;
var cpuAudioFactor = 32;
rom = new RAM(0x4000).mem;
// sample: [0xe,0x0,0x6,0x0,0x78,0xb9,0x30,0x06,0xa9,0xd3,0x00,0x04,0x18,0xf6,0x0c,0x79,0xd6,0xff,0x38,0xee,0x76,0x18,0xea];
rom.fill(0x76); // HALT opcodes
function fillBuffer() {
var t = tstates / cpuAudioFactor;
while (last_tstate < t) {
current_buffer[last_tstate*2] = dac;
current_buffer[last_tstate*2+1] = dac;
last_tstate++;
}
}
function start() {
ram = new RAM(0x400);
membus = {
read: newAddressDecoder([
[0x0000, 0x3fff, 0x3fff, function(a) { return rom ? rom[a] : null; }],
[0x4000, 0x7fff, 0x3ff, function(a) { return ram.mem[a]; }]
]),
write: newAddressDecoder([
[0x4000, 0x7fff, 0x3ff, function(a,v) { ram.mem[a] = v; }],
]),
isContended: function() { return false; },
};
iobus = {
read: function(addr) {
return command & 0xff;
},
write: function(addr, val) {
dac = (val & 0xff) << 8;
fillBuffer();
}
};
cpu = new exports.Z80();
cpu.connectMemoryBus(membus);
cpu.connectIOBus(iobus);
current_buffer = new Int16Array(bufferLength);
console.log('started audio');
}
function timerCallback() {
tstates = 0;
last_tstate = 0;
var numStates = Math.floor(bufferLength * cpuAudioFactor / numChannels);
while (tstates < numStates) {
tstates += cpu.advanceInsn();
}
tstates = 0;
fillBuffer();
postMessage({samples:current_buffer});
if (!cpu.saveState().halted) {
curTime += timerPeriod;
var dt = curTime - new Date().getTime();
if (dt < 0) dt = 0;
timer = setTimeout(timerCallback, dt);
} else {
timer = 0;
//console.log("HALT @ " + cpu.getPC());
}
}
function reset() {
if (!bufferLength) return;
cpu.reset();
if (!timer) {
curTime = new Date().getTime() - timerPeriod*4;
timerCallback();
}
}
onmessage = function(e) {
if (e && e.data) {
if (e.data.command) {
command = e.data.command & 0xff;
reset();
} else if (e.data.sampleRate) {
console.log(e.data);
sampleRate = e.data.sampleRate;
bufferLength = numChannels*sampleRate*timerPeriod/1000;
start();
reset();
} else if (e.data.rom) {
rom = e.data.rom;
command = 0x0;
reset();
}
}
}
/**
0000 56 _main::
57 ;<stdin>:10:
0000 0E 00 [ 7] 58 ld c,#0x00
59 ;<stdin>:11:
0002 60 00111$:
0002 06 00 [ 7] 61 ld b,#0x00
0004 62 00104$:
0004 78 [ 4] 63 ld a,b
0005 B9 [ 4] 64 cp a,c
0006 30 06 [12] 65 jr NC,00107$
0008 A9 [ 4] 66 xor a, c
0009 D3 00 [11] 67 out (_dac),a
000B 04 [ 4] 68 inc b
000C 18 F6 [12] 69 jr 00104$
000E 70 00107$:
71 ;<stdin>:10:
000E 0C [ 4] 72 inc c
000F 79 [ 4] 73 ld a,c
0010 D6 FF [ 7] 74 sub a, #0xff
0012 38 EE [12] 75 jr C,00111$
76 ;<stdin>:13:
0014 18 EA [12] 77 jr _main
**/

View File

@ -1193,7 +1193,7 @@ export abstract class BaseWASMMachine {
}
async loadWASM() {
// fetch WASM
var wasmResponse = await fetch('wasm/'+this.prefix+'.wasm');
var wasmResponse = await fetch('res/'+this.prefix+'.wasm');
var wasmBinary = await wasmResponse.arrayBuffer();
var wasmCompiled = await WebAssembly.compile(wasmBinary);
var wasmResult = await WebAssembly.instantiate(wasmCompiled);
@ -1201,7 +1201,7 @@ export abstract class BaseWASMMachine {
this.exports = wasmResult.exports;
this.exports.memory.grow(32);
// fetch BIOS
var biosResponse = await fetch('wasm/'+this.prefix+'.bios');
var biosResponse = await fetch('res/'+this.prefix+'.bios');
var biosBinary = await biosResponse.arrayBuffer();
const cBIOSPointer = this.exports.malloc(0x5000);
const srcArray = new Uint8Array(biosBinary);

View File

@ -77,6 +77,8 @@ var TOOL_TO_SOURCE_STYLE = {
'js': 'javascript',
'xasm6809': 'z80',
'cmoc': 'text/x-csrc',
'yasm': 'gas',
'smlrc': 'text/x-csrc',
}
function gaEvent(category:string, action:string, label?:string, value?:string) {
@ -1496,11 +1498,11 @@ function addFileToProject(type, ext, linefn) {
alertError("Can't insert text in this window -- switch back to main file");
}
}
// TODO: lwtools and smaller c
function _addIncludeFile() {
var fn = getCurrentMainFilename();
var tool = platform.getToolForFilename(fn);
if (fn.endsWith(".c") || tool == 'sdcc' || tool == 'cc65')
if (fn.endsWith(".c") || tool == 'sdcc' || tool == 'cc65' || tool == 'cmoc' || tool == 'smlrc')
addFileToProject("Header", ".h", (s) => { return '#include "'+s+'"' });
else if (tool == 'dasm' || tool == 'zmac')
addFileToProject("Include File", ".inc", (s) => { return '\tinclude "'+s+'"' });
@ -1515,9 +1517,9 @@ function _addIncludeFile() {
function _addLinkFile() {
var fn = getCurrentMainFilename();
var tool = platform.getToolForFilename(fn);
if (fn.endsWith(".c") || tool == 'sdcc' || tool == 'cc65')
if (fn.endsWith(".c") || tool == 'sdcc' || tool == 'cc65' || tool == 'cmoc' || tool == 'smlrc')
addFileToProject("Linked C (or .s)", ".c", (s) => { return '//#link "'+s+'"' });
else if (fn.endsWith("asm") || fn.endsWith(".s") || tool == 'ca65')
else if (fn.endsWith("asm") || fn.endsWith(".s") || tool == 'ca65' || tool == 'lwasm')
addFileToProject("Linked ASM", ".inc", (s) => { return ';#link "'+s+'"' });
else
alertError("Can't add linked file to this project type (" + tool + ")");

View File

@ -15,6 +15,7 @@ const APPLE2_PRESETS = [
{id:'hgrtest.a', name:"HGR Test (ASM)"},
{id:'conway.a', name:"Conway's Game of Life (ASM)"},
{id:'lz4fh.a', name:"LZ4FH Graphics Compression (ASM)"},
// {id:'zap.dasm', name:"ZAP! (ASM)"},
// {id:'tb_6502.s', name:'Tom Bombem (assembler game)'},
];

View File

@ -13,6 +13,7 @@ import { NullProbe, Probeable, ProbeAll } from "../common/devices";
// http://vide.malban.de/help/vectrex-tutorial-ii-starting-with-bios
// http://www.playvectrex.com/designit/chrissalo/bios.asm
// https://www.6809.org.uk/asm6809/doc/asm6809.shtml
// http://www.playvectrex.com/
var VECTREX_PRESETS = [
{ id: 'hello.xasm', name: 'Hello World (ASM)' },

151
src/platform/x86.ts Normal file
View File

@ -0,0 +1,151 @@
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString, BaseDebugPlatform } from "../common/baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, KeyFlags, EmuHalt, ControllerPoller } from "../common/emu";
import { hex, lpad, lzgmini, byteArrayToString } from "../common/util";
import { CodeAnalyzer_nes } from "../common/analysis";
import { SampleAudio } from "../common/audio";
import { ProbeRecorder } from "../common/recorder";
import { NullProbe, Probeable, ProbeAll } from "../common/devices";
import { loadScript } from "../ide/ui";
// PC emulator: https://github.com/copy/v86
declare var V86Starter : any;
declare var V86 : any;
declare var CPU : any;
declare var fatfs : any;
const PC_PRESETS = [
{id:'hello.asm', name:'Hello World (ASM)'},
{id:'mandelg.asm', name:'Mandelbrot (ASM)'},
{id:'snake.c', name:'Snake Game (C)'},
];
class FATFSArrayBufferDriver {
buffer : ArrayBuffer;
data : DataView;
sectorSize : number;
numSectors : number;
constructor(buffer : ArrayBuffer) {
this.buffer = buffer;
this.data = new DataView(this.buffer);
this.sectorSize = 512;
this.numSectors = this.buffer.byteLength / this.sectorSize;
}
readSectors(sector, dest, cb) {
var ofs = this.sectorSize * sector;
for (var i=0; i<dest.length; i++) {
dest[i] = this.data.getUint8(i + ofs);
}
//console.log('read', sector, dest, cb);
cb(null);
}
writeSectors(sector, data, cb) {
var ofs = this.sectorSize * sector;
for (var i=0; i<data.length; i++) {
this.data.setUint8(i + ofs, data[i]);
}
//console.log('write', sector, data, cb);
cb(null);
}
}
class X86PCPlatform implements Platform {
mainElement;
video;
emulator;
v86;
fda_image;
fda_driver;
fda_fs;
constructor(mainElement) {
//super();
this.mainElement = mainElement;
}
getToolForFilename(s: string): string {
if (s.endsWith(".c")) return "smlrc";
return "yasm";
}
getDefaultExtension(): string {
return "asm";
}
getPresets() {
return PC_PRESETS;
}
pause(): void {
if (this.isRunning()) this.emulator.stop();
}
resume(): void {
if (!this.isRunning()) this.emulator.run();
}
reset() {
this.emulator.restart();
}
isRunning() {
return this.emulator.is_running();
}
loadROM(title: string, rom: any) {
this.fda_fs.writeFile('main.exe', rom, {encoding:'binary'}, (e) => {
if (e) throw e;
else this.reset();
});
}
async start() {
await loadScript('./lib/libv86.js');
await loadScript('./lib/fatfs.js');
this.video = new RasterVideo(this.mainElement,640,480,{overscan:false});
this.video.create();
var div = document.createElement('div');
div.classList.add('pc-console');
div.classList.add('emuvideo');
this.mainElement.appendChild(div);
this.emulator = new V86Starter({
memory_size: 2 * 1024 * 1024,
vga_memory_size: 1 * 1024 * 1024,
screen_container: this.mainElement,
bios: {
url: "./res/seabios.bin",
},
vga_bios: {
url: "./res/vgabios.bin",
},
fda: {
url: "./res/freedos722.img",
size: 737280,
},
autostart: true,
});
return new Promise<void>( (resolve, reject) => {
this.emulator.add_listener("emulator-ready", () => {
console.log("emulator ready");
console.log(this.emulator);
this.v86 = this.emulator.v86;
this.fda_image = this.v86.cpu.devices.fdc.fda_image;
this.fda_driver = new FATFSArrayBufferDriver(this.fda_image.buffer);
this.fda_fs = fatfs.createFileSystem(this.fda_driver);
resolve();
});
});
}
readAddress(addr:number) {
return this.v86.cpu.mem8[addr];
}
getMemoryMap() { return { main:[
{name:'Real Mode IVT',start:0x0,size:0x400,type:'ram'},
{name:'BIOS Data Area',start:0x400,size:0x100,type:'ram'},
{name:'User RAM',start:0x500,size:0x80000-0x500,type:'ram'},
{name:'Extended BIOS Data Area',start:0x80000,size:0x20000,type:'ram'},
{name:'Video RAM',start:0xa0000,size:0x20000,type:'ram'},
{name:'Video BIOS',start:0xc0000,size:0x8000,type:'rom'},
{name:'BIOS Expansions',start:0xc8000,size:0x28000,type:'rom'},
{name:'PC BIOS',start:0xf0000,size:0x10000,type:'rom'},
] } };
}
PLATFORMS['x86'] = X86PCPlatform;

22
src/worker/wasm/smlrc.js Normal file

File diff suppressed because one or more lines are too long

BIN
src/worker/wasm/smlrc.wasm Normal file

Binary file not shown.

5051
src/worker/wasm/yasm.js Normal file

File diff suppressed because it is too large Load Diff

BIN
src/worker/wasm/yasm.wasm Normal file

Binary file not shown.

View File

@ -259,6 +259,8 @@ var PLATFORM_PARAMS = {
extra_compile_args: ['--vectrex'],
extra_link_args: ['-svectrex.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
},
'x86': {
}
};
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
@ -2256,6 +2258,123 @@ function linkLWLINK(step:BuildStep) {
}
}
// http://www.techhelpmanual.com/829-program_startup___exit.html
function compileSmallerC(step:BuildStep) {
loadNative("smlrc");
var params = step.params;
// stderr
var re_err1 = /^Error in "[/]*(.+)" [(](\d+):(\d+)[)]/;
var errors : WorkerError[] = [];
var errline = 0;
var errpath = step.path;
function match_fn(s) {
var matches = re_err1.exec(s);
if (matches) {
errline = parseInt(matches[2]);
errpath = matches[1];
} else {
errors.push({
line:errline,
msg:s,
path:errpath,
});
}
}
gatherFiles(step, {mainFilePath:"main.c"});
var destpath = step.prefix + '.asm';
if (staleFiles(step, [destpath])) {
var args = ['-seg16',
//'-nobss',
'-no-externs',
step.path, destpath];
var smlrc = emglobal.smlrc({
instantiateWasm: moduleInstFn('smlrc'),
noInitialRun:true,
//logReadFiles:true,
print:match_fn,
printErr:match_fn,
});
// load source file and preprocess
var code = getWorkFileAsString(step.path);
var preproc = preprocessMCPP(step, null);
if (preproc.errors) return preproc;
else code = preproc.code;
// set up filesystem
var FS = smlrc['FS'];
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
FS.writeFile(step.path, code);
fixParamsWithDefines(step.path, params);
if (params.extra_compile_args) {
args.unshift.apply(args, params.extra_compile_args);
}
execMain(step, smlrc, args);
if (errors.length)
return {errors:errors};
var asmout = FS.readFile(destpath, {encoding:'utf8'});
putWorkFile(destpath, asmout);
}
return {
nexttool:"yasm",
path:destpath,
args:[destpath],
files:[destpath],
};
}
function assembleYASM(step:BuildStep) {
loadNative("yasm");
var errors = [];
gatherFiles(step, {mainFilePath:"main.asm"});
var objpath = step.prefix+".exe";
var lstpath = step.prefix+".lst";
var mappath = step.prefix+".map";
if (staleFiles(step, [objpath])) {
var args = [ '-X', 'vc',
'-a', 'x86', '-f', 'dosexe', '-p', 'nasm',
'-D', 'freedos',
//'-g', 'dwarf2',
//'-I/share/asminc',
'-o', objpath, '-l', lstpath, '--mapfile='+mappath,
step.path];
// return yasm/*.ready*/
var YASM = emglobal.yasm({
instantiateWasm: moduleInstFn('yasm'),
noInitialRun:true,
//logReadFiles:true,
print:print_fn,
printErr:msvcErrorMatcher(errors),
});
var FS = YASM['FS'];
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
//fixParamsWithDefines(step.path, step.params);
execMain(step, YASM, args);
if (errors.length)
return {errors:errors};
var objout, lstout, mapout;
objout = FS.readFile(objpath, {encoding:'binary'});
lstout = FS.readFile(lstpath, {encoding:'utf8'});
mapout = FS.readFile(mappath, {encoding:'utf8'});
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
//putWorkFile(mappath, mapout);
if (!anyTargetChanged(step, [objpath]))
return;
var symbolmap = {};
var segments = [];
var lines = parseListing(lstout, /\s*(\d+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(.+)/i, 1, 2, 3);
var listings : CodeListingMap = {};
listings[lstpath] = {lines:lines, text:lstout};
return {
output:objout, //.slice(0),
listings:listings,
errors:errors,
symbolmap:symbolmap,
segments:segments
};
}
}
////////////////////////////
var TOOLS = {
@ -2281,6 +2400,8 @@ var TOOLS = {
'jsasm': compileJSASMStep,
'zmac': assembleZMAC,
'nesasm': assembleNESASM,
'smlrc': compileSmallerC,
'yasm': assembleYASM,
'bataribasic': compileBatariBasic,
'markdown': translateShowdown,
'js': runJavascript,

View File

@ -11,6 +11,8 @@ global.onmessage({data:{preload:'cc65', platform:'nes'}});
global.onmessage({data:{preload:'ca65', platform:'nes'}});
global.onmessage({data:{preload:'cc65', platform:'apple2'}});
global.onmessage({data:{preload:'ca65', platform:'apple2'}});
global.onmessage({data:{preload:'cc65', platform:'c64'}});
global.onmessage({data:{preload:'ca65', platform:'c64'}});
global.onmessage({data:{preload:'sdcc'}});
// TODO: check msg against spec
@ -313,11 +315,10 @@ describe('Worker', function() {
it('should assemble CA65', function(done) {
compile('ca65', ';#define LIBARGS ,\n\t.segment "HEADER"\n\t.segment "STARTUP"\n\t.segment "CHARS"\n\t.segment "VECTORS"\n\t.segment "SAMPLES"\n\t.segment "CODE"\n\tlda #0\n\tsta $1\n', 'nes', done, 131088, 2);
});
/* TODO
it('should compile C64 cc65 skeleton', function(done) {
var csource = ab2str(fs.readFileSync('presets/c64/skeleton.cc65'));
compile('cc65', csource, 'c64.wasm', done, 49152, 80, 0);
compile('cc65', csource, 'c64.wasm', done, 2753, 2, 0);
});
*/
// TODO: vectrex, x86
});