Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPM and Typescript support #647

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

ambientlight
Copy link
Contributor

@ambientlight ambientlight commented May 4, 2022

  • package.json with base version set to 0.0.1
  • typescript definitions for V86Starter exposed APIs

I have used v86 as a package name

types/index.d.ts Outdated
screen_dummy?: boolean
}

declare class V86Starter {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called V86, V86Starter is on its way out (would also be good to rename V86StarterOptions and other mentions).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copy: I am able to rename the interfaces, not the class declarations, or do you wish to rename the V86Starter to V86 inside the codebase?

package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
types/index.d.ts Outdated Show resolved Hide resolved
types/index.d.ts Outdated Show resolved Hide resolved
types/index.d.ts Outdated
* @param {number} offset
*/
write_memory(blob: Array<number> | Uint8Array, offset: number): void
}
Copy link
Owner

@copy copy May 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is brilliant, thanks! Did you write this from scratch or is (at least partially) generated from some of the source files? I'm a bit worried this might become out-of-date over time, although that shouldn't be a blocker for merging for now.

(nitpick: Add a newline at the end of the file)

Minor suggestion: Could you add a test, something very simple (e.g., similar to tests/api/clean-shutdown.js) that demonstrates loading this file with ts-node and using V86 from typescript? Doesn't need to be part of CI or mentioned in the readme yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copy: this is generated from your JSDoc @param types. My approach was to scan through your starter.js and add typings for members with @exports, write_memory or read_memory had no @exports on them, but It felt essential.

Minor suggestion: Could you add a test, something very simple (e.g., similar to tests/api/clean-shutdown.js) that demonstrates loading this file with ts-node and using V86 from typescript? Doesn't need to be part of CI or mentioned in the readme yet.

sure.

types/index.d.ts Outdated Show resolved Hide resolved
types/index.d.ts Outdated Show resolved Hide resolved
types/index.d.ts Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@giulioz
Copy link
Contributor

giulioz commented May 6, 2022

@ambientlight @copy instead of publishing more mirrors of v86 on NPM, why don't we all maintain the same one? Otherwise it's easy for people to get unmaintained versions and be frustrated with the library.
I can share the write access if anybody wants.

Regarding this PR: very nice work with the TS typings! I think, though, that when publishing it on NPM it should be a module, so that users can import it using bundlers. That's the reason of existence of my fork, but I don't know if @copy wants to have his original work as module also.

@ambientlight
Copy link
Contributor Author

ambientlight commented May 6, 2022

@ambientlight @copy instead of publishing more mirrors of v86 on NPM, why don't we all maintain the same one? Otherwise it's easy for people to get unmaintained versions and be frustrated with the library.
I can share the write access if anybody wants.

That would be the best, certainly. I am fine with any way @giulioz and @copy think it is best, this draft is what I added few month back for my own experiment.

Regarding this PR: very nice work with the TS typings! I think, though, that when publishing it on NPM it should be a module, so that users can import it using bundlers. That's the reason of existence of my fork, but I don't know if @copy wants to have his original work as module also.

So here is why this PR is a draft:

I am personally consuming it via importmaps for now:

<script type="importmap">
  {
    "imports": {
      "@glasssh/v86": "/libv86.js",
      "xterm": "./xterm.js",
      "xterm-addon-fit": "./xterm-addon-fit.js"
    }
  }
</script>

I think what we can do is probably make closure compiler produce a UMD bundle (I haven't checked @giulioz's fork yet), the UMD can be then distributed and served with both NPM and CDN style. What do you think? I want to do this in the scope of this PR and then make sure it is smooth with bundling via something like rollup or webpack.

I don't remember how to make typescript work smoothly with global-scoped and module at the same time, but that's my idea for now. I see other libraries just shipped typescript for moduled bundle and then I need to reimport myself the definitions to the global namespace via something like:
global.d.ts:

declare namespace globalThis {
    export { Terminal } from 'xterm'
}

@ambientlight
Copy link
Contributor Author

@copy: I think what was also discussed in a gitter, is that this PR should also add the github action that builds of master and publishes to npm with a revision number (git rev-list --count master). Quick question: what is this revision number used for? Should this action run when version tags are pushed?

@giulioz
Copy link
Contributor

giulioz commented May 6, 2022

I think what we can do is probably make closure compiler produce a UMD bundle (I haven't checked @giulioz's fork yet), the UMD can be then distributed and served with both NPM and CDN style. What do you think? I want to do this in the scope of this PR and then make sure it is smooth with bundling via something like rollup or webpack.

I've tried a lot with it and unfortunately it's quite difficult to produce module-friendly javascript with closure compiler, so I ended up removing it completely and using Rollup instead. I also have performed some tests and the performance benefits introduced by closure compiler are none, so there should be no harm in doing that.

The main problem is that v86 relies a lot on working with the global scope, which is difficult to handle when using bundler and typescript. My solution in the fork was to explicitly import and export everything so that it can avoid polluting the global namespace.

@copy
Copy link
Owner

copy commented May 6, 2022

instead of publishing more mirrors of v86 on NPM, why don't we all maintain the same one?

Absolutely agreed. Let's do this now, since we're all here.

That's the reason of existence of my fork, but I don't know if @copy wants to have his original work as module also.

I would love to have your changes in the repository. If we can remove the horrible loader code in debug.html — even better. My only requirement is no mandatory npm dependencies, the release build should go through closure compiler, and no build step for debug.html.

I think what we can do is probably make closure compiler produce a UMD bundle (I haven't checked @giulioz's fork yet), the UMD can be then distributed and served with both NPM and CDN style. What do you think? I want to do this in the scope of this PR and then make sure it is smooth with bundling via something like rollup or webpack.

That would be great, and I'd strongly prefer this over using rollup inside of this repository (see the next paragraph).

I've tried a lot with it and unfortunately it's quite difficult to produce module-friendly javascript with closure compiler, so I ended up removing it completely and using Rollup instead. I also have performed some tests and the performance benefits introduced by closure compiler are none, so there should be no harm in doing that.

The main problem is that v86 relies a lot on working with the global scope, which is difficult to handle when using bundler and typescript. My solution in the fork was to explicitly import and export everything so that it can avoid polluting the global namespace.

Removing Closure Compiler entirely is not really an option for me. It does bundling, minifying, basic linting, type-checking, and is much more stable than most things in the npm ecosystem (and it can be installed through npm for the folks who don't want to install java). We could use Rollup only for the npm build, but having a single build system would obviously be nicer.

I'm not opposed to refactoring the global stuff if it makes bundling easier.

I think what was also discussed in a gitter, is that this PR should also add the github action that builds of master and publishes to npm with a revision number (git rev-list --count master). Quick question: what is this revision number used for? Should this action run when version tags are pushed?

It is used as the version number (perhaps prefixed with 0.0., so if we decide to do versioning, we can bump the prefix). No need to handle version tags for now.

@giulioz
Copy link
Contributor

giulioz commented May 6, 2022

Removing Closure Compiler entirely is not really an option for me. It does bundling, minifying, basic linting, type-checking, and is much more stable than most things in the npm ecosystem (and it can be installed through npm for the folks who don't want to install java). We could use Rollup only for the npm build, but having a single build system would obviously be nicer.

True, but closure compiler has a really hard time producing "modern" js understandable by bundlers. Would you be ok with having both of them?

I've tried to use closure to make importable modules, but the documentation about that non-existent. Do you have any info for that?

@ambientlight
Copy link
Contributor Author

@giulioz, @copy: Thanks for great input, understood.

@giulioz: I was thinking more of a scaffold like at https://stackoverflow.com/a/39862418/2380455, however I have limited experience with closure compiler.

I'm seeing a non-minified libv86-debug.js having the following structure:

image

It should fit in to the UMD scaffold if we substitute those with referenced scaffold in the SO's answer. The UMD should then be consumable with tooling like bundlers that understand commonjs or amd. This is quite hypothetic though as I haven't tried it yet.

@copy
Copy link
Owner

copy commented May 7, 2022

True, but closure compiler has a really hard time producing "modern" js understandable by bundlers. Would you be ok with having both of them?

I'm not strongly opposed to having both, but it would be nicer if everything was built using a single tool.

I've tried to use closure to make importable modules, but the documentation about that non-existent. Do you have any info for that?

There is some support for exports with the @export annotation. It prevents renaming and generates this code:

V86Starter.prototype.run = function() {
   ...
};
goog.exportProperty(V86Starter.prototype, "run", V86Starter.prototype.run);

(where goog.exportProperty is empty by default, and I believe you could define it yourself).

Indeed there seems to be no support for generating export statements (which I guess would be the most compatible method?)

I see two options:

  1. We put everything that should be exported into a (global?) object and append export default … to the output file
  2. We modify the existing export code to make it compatible with whatever bundlers expect

@copy copy mentioned this pull request May 12, 2022
@ambientlight
Copy link
Contributor Author

ambientlight commented May 13, 2022

I see two options: 1. We put everything that should be exported into a (global?) object and append export default … to the output file 2. We modify the existing export code to make it compatible with whatever bundlers expect

@copy, @giulioz : 2 seems to be cleaner (removing those window[...] = ), together with a change to

v86/Makefile

Line 136 in d685053

--output_wrapper ';(function(){%output%}).call(this);'\
with some kind of a scaffold that collects the definitions seems to do the trick:

--output_wrapper 'var v86=(function(){%output% return {V86Starter,CPU}}).call(this);if(typeof module === 'object' && module.exports){module.exports = v86;}'\

need to play around with this more, maybe there is a cleaner way for this.

But without code changes, I have failed with my original idea of tampering around the compiled bundle via changing that --output-wrapper. Generated code adds some magic that finds the global scope and then attaches the definitions just as it should, or I am missing something. From what I see, existing output wrapper helps for closure compiler to not inject its polyfills and internal flags into global scope. But I don't seem to find a way how to hijack it and have an empty module assumed instead of global. If we don't perform code modification, I am unsure to get rid of having v86 in global scope. If we are ok with global scope however, we can provide guides to the user to make their bundling work in webpack and rollup probably. It needs tweaks but webpack is quite straightforward for example. the webpack 5 configuration I have used to bundle v86 is something like:

const path = require('path');
const isProduction = process.env.NODE_ENV == 'production';
const config = {
    entry: './src/index.wp.ts',
    output: {
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx|tsx|ts)$/i,
                loader: 'babel-loader',
                exclude: ['/node_modules/'],
            }
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
        fallback: {
            "crypto": false,
            "perf_hooks": false,
            "fs": false,
            "path": false
        }
    },
    externals: {
        './libwabt.js': 'WabtModule',
        './capstone-x86.min.js': 'MCapstone'
    }
};

module.exports = () => {
    if (isProduction) {
        config.mode = 'production';
    } else {
        config.mode = 'development';
    }
    return config;
};

…v86,V86Starter,CPU,MemoryFileStorage,ServerFileStorageWrapper
@kyr0
Copy link

kyr0 commented May 14, 2022

I'm testing your typings and I found one missing in V86StarterOptions. At least in the version I use, you can set wasm_fn and I use that option because I import the WASM module this way via Vite / esbuild:

import v86Wasm from "v86/build/v86.wasm";

Now the typings result in:

Argument of type '{ wasm_fn: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>; memory_size: number; vga_memory_size: number; screen_container: HTMLElement; bios: { ...; }; vga_bios: { ...; }; fda: { ...; }; autostart: true; }' is not assignable to parameter of type 'V86StarterOptions'.
  Object literal may only specify known properties, and 'wasm_fn' does not exist in type 'V86StarterOptions'

To fix this, you can simply add to V86StarterOptions:

    /**
     * Reference to the v86 wasm exorted function.
     * @default undefined 
     */
    wasm_fn: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>

@kyr0
Copy link

kyr0 commented May 14, 2022

Btw, I'm working on the "internal" typings as well. I use them because I, despite reading the mem8, also read the registers of the CPU, reg32, directly (for example).

At the moment, v86 as a property of V86Starter is missing. I'd like to share my typings as well. I have it structured like this (it's a ton of typings, I'm outlining here):

export class CPU {
    ...
}

declare class V86 {
    ...
    cpu: CPU
    ...
}

declare class V86Starter {
   ...
   v86: V86
   ...
}

What would be the best way to join this PR? Would you guys like to merge this first and I open another one on top of this?

btw, another one: If someone is testing this and already using one of the existing NPM packages out there, they will run in the error below until all of the definitions are wrapped in: declare module 'v86' { ... } (or whatever the npm module name they use is).

Bildschirmfoto 2022-05-14 um 09 22 40

Of course this wouldn't be a problem for the when this package is released, as long as we include types or typings field pointing to the .d.ts file in package.json.

@ambientlight
Copy link
Contributor Author

ambientlight commented May 14, 2022

@kyr0: thanks for the feedback, I suspect the typings are incomplete - as at the moment of me writing them - I wasn't much familiar with v86, so anything is certainly very helpful for this PR, thanks.

What would be the best way to join this PR? Would you guys like to merge this first and I open another one on top of this?

Sure, you can just add your typing directly to this PR, I can give your write access to the fork (I have sent you the invite)

btw, another one: If someone is testing this and already using one of the existing NPM packages out there, they will run in the error below until all of the definitions are wrapped in: declare module 'v86' { ... } (or whatever the npm module name they use is).

I will have this addressed now as per original @copy's comment. For now the typing here assume global scope.

@copy: I also want to clarify on what should be exposed in total (I just scanned the codebase for those exports snippets). Is it like?:

import { v86, V86Starter, CPU, MemoryFileStorage, ServerFileStorageWrapper } from "v86"

@@ -13,7 +13,7 @@
"build": "npm run build:all_debug & npm run build:all",
"build:all_debug": "make all-debug",
"build:all": "make all",
"build:release": "make build/libv86.js build/v86.wasm build/v86-fallback.wasm",
"build:release": "make build/libv86.js build/v86.wasm build/v86-fallback.wasm build/capstone-x86.min.js build/libwabt.js",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are only for debugging, I don't think they should be included in a release build.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the bundlers will parse the debug.js requires by defaults and would try to bundle those - if we don’t distribute them the bundlers would complain unless we add explicit externs, my thought here was is to make bundling succeed on defaults, but we can also do something so that the compiled code does not contain require() on those two libraries.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. If you add if(!DEBUG) return; to the beginning of the functions that call require that should cause closure compiler to remove those calls. Alternatively, I'd fine with removing the require call entirely. I haven't used those libraries from node in a long time anyway.

@copy
Copy link
Owner

copy commented May 14, 2022

@copy: I also want to clarify on what should be exposed in total (I just scanned the codebase for those exports snippets). Is it like?:
import { v86, V86Starter, CPU, MemoryFileStorage, ServerFileStorageWrapper } from "v86"

V86Starter has been renamed to V86, and I don't want to expose the name v86 (I actually want to get rid of it).

@kyr0
Copy link

kyr0 commented May 14, 2022

@ambientlight You're welcome. I'll add a few hundred more lines tomorrow. Btw, depending on the configuration (no implicit any) tsc is a bit unhappy about functions without explicit return type like this one here: https://github.com/copy/v86/pull/647/files#diff-093ad82a25aee498b11febf1cdcb6546e4d223ffcb49ed69cc275ac27ce0ccceR515 -> serial_send_bytes(serial: number, data: Uint8Array) (but there are more); I fixed most of them them on my side, but maybe you wanna add them for the ones you might add on top of the existing ones?

@kyr0
Copy link

kyr0 commented May 17, 2022

Hey @ambientlight @copy , sry, I'm sick since yesterday. I'll get back to this once I've recovered. I hope for tomorrow..

@Zaid-maker
Copy link

Hey @ambientlight @copy , sry, I'm sick since yesterday. I'll get back to this once I've recovered. I hope for tomorrow..

Are u good now broo??

@kyr0
Copy link

kyr0 commented May 22, 2024

@copy @Zaid-maker @ambientlight My most recent TypeScript types code in 2023 was:

declare global {

    interface Window { emulator: V86Starter; }

   /** set when v86 is built with debug mode enabled */
    export const DEBUG: boolean;

    export type V86Image = 
    | { url: string } 
    | { buffer: ArrayBuffer }
    | { url: string, async: false, size?: number }
    | { url: string, async: true, size: number }

    /**
     * A 9p filesystem is supported by the emulator, using a virtio transport. Using it, files can be exchanged with the guest OS
     * If `basefs` and `baseurl` are omitted, an empty 9p filesystem is created.
     */
    export interface Filesystem9pOptions {
    /**
     * json file created using [fs2json](https://github.com/copy/v86/blob/master/tools/fs2json.py). 
     */
    baseurl?: string,
    
    /**
     * The base url is the prefix of a url from which the files are available. 
     * For instance, if the 9p filesystem has a file `/bin/sh`, that file must be accessible from http://localhost/9p/base/bin/sh
     */
    basefs?: string
    }

    export enum LogLevel {
    LOG_ALL =    -1,
    LOG_NONE =   0,
    LOG_OTHER =  0x000001,
    LOG_CPU =    0x000002,
    LOG_FPU =    0x000004,
    LOG_MEM =    0x000008,
    LOG_DMA =    0x000010,
    LOG_IO =     0x000020,
    LOG_PS2 =    0x000040,
    LOG_PIC =    0x000080,
    LOG_VGA =    0x000100,
    LOG_PIT =    0x000200,
    LOG_MOUSE =  0x000400,
    LOG_PCI =    0x000800,
    LOG_BIOS =   0x001000,
    LOG_FLOPPY = 0x002000,
    LOG_SERIAL = 0x004000,
    LOG_DISK =   0x008000,
    LOG_RTC =    0x010000,
    LOG_HPET =   0x020000,
    LOG_ACPI =   0x040000,
    LOG_APIC =   0x080000,
    LOG_NET =    0x100000,
    LOG_VIRTIO = 0x200000,
    LOG_9P =     0x400000,
    LOG_SB16 =   0x800000
    } 

    export enum BootOrder {
    CD_FLOPPY_HARDDISK = 0x213,
    CD_HARDDISK_FLOPPY = 0x123,
    FLOPPY_CD_HARDDISK = 0x231,
    FLOPPY_HARDDISK_CD = 0x321,
    HARDDISK_CD_FLOPPY = 0x132
    }

    export interface BusListener {
        fn: Function
        this_value: V86
    }

    export class BusConnector {
    constructor()

    pair: BusConnector
    listeners: {
        [eventName: string]: Array<BusListener>,
    }
    
    /**
     * @param {string} name
     * @param {function(?)} fn
     * @param {Object} this_value
     */
    register(name: string, fn: Function, this_value: Object): void

    /**
     * Unregister one message with the given name and callback
     *
     * @param {string} name
     * @param {function()} fn
     */
    unregister(name: string, fn: Function): void

    /**
     * Send ("emit") a message
     *
     * @param {string} name
     * @param {*=} value
     * @param {*=} unused_transfer
     */
    send(name: string, value: any, unused_transfer?: any): void

    /**
     * Send a message, guaranteeing that it is received asynchronously
     *
     * @param {string} name
     * @param {Object=} value
     */
    send_async(name: string, value: any): void
    }

    /**
     * Custom emulated ethernet card adapter (pass in V86StarterOptions)
     * @see https://github.com/copy/v86/blob/master/src/browser/network.js
     */
    export class NetworkAdapter {
    constructor(bus: BusConnector)

    handle_message(event: any & { data: string | ArrayBufferLike | Blob | ArrayBufferView }): void
    handle_close(event: any): void
    handle_open(event: any): void
    handle_error(event: any): void
    destroy(event: any): void
    connect(): void
    send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void
    change_proxy(url: string): void
    }

    export type V86AutomaticStep =
    | { 
        /** wait for x seconds */ 
        sleep: number
    } 
    | { 
        /** wait until vga_text is present on the screen */
        vga_text: string 
    } 
    | { 
        /** text or scancodes to send */
        keyboard_send: number[] | string 
    } 
    | {
        /** callback to execute */
        call: () => void 
    }

    export interface BIOS {
        main: ArrayBuffer
        vga: ArrayBuffer
    }

    export interface PackedMemory {
        bitmap: Uint8Array
        packed_memory: Uint8Array
    }

    export class CPU {
        wm: {
            exports: WebAssembly.Exports,
            wasm_table: WebAssembly.Table,
        }
        wasm_memory: WebAssembly.Memory
        memory_size: Uint32Array

        mem8: Uint8Array
        mem32s: Int32Array
        
        segment_is_null: Uint8Array
        segment_offsets: Int32Array
        segment_limits: Uint32Array

        /**
         * Wheter or not in protected mode
         */
        protected_mode: Int32Array

        idtr_size: Int32Array
        idtr_offset: Int32Array

        /**
         * global descriptor table register
         */
        gdtr_size: Int32Array
        gdtr_offset: Int32Array

        tss_size_32: Int32Array

        /*
        * whether or not a page fault occured
        */
        page_fault: Uint32Array
        cr: Int32Array

        // current privilege level
        cpl: Uint8Array

        // current operand/address size
        is_32: Int32Array

        stack_size_32: Int32Array

        /**
         * Was the last instruction a hlt?
         */
        in_hlt: Uint8Array

        last_virt_eip: Int32Array
        eip_phys: Int32Array

        sysenter_cs: Int32Array
        sysenter_esp: Int32Array
        sysenter_eip: Int32Array

        prefixes: Int32Array
        flags: Int32Array

        /**
         * bitmap of flags which are not updated in the flags variable
         * changed by arithmetic instructions, so only relevant to arithmetic flags
         */
        flags_changed: Int32Array

        /**
         * enough infos about the last arithmetic operation to compute eflags
         */
        last_op1: Int32Array
        last_op_size: Int32Array
        last_result: Int32Array

        current_tsc: Uint32Array

        instruction_pointer: Int32Array
        previous_ip: Int32Array

        /**
         * configured by guest
         */
        apic_enabled: Uint8Array

        /**
         * configured when the emulator starts (changes bios initialisation)
         */
        acpi_enabled: Uint8Array

        bios: BIOS

        instruction_counter: Uint32Array

        reg32: Int32Array

        fpu_st: Int32Array

        fpu_stack_empty: Uint8Array

        fpu_stack_ptr: Uint8Array

        fpu_control_word: Uint16Array

        fpu_status_word: Uint16Array

        fpu_ip: Int32Array

        fpu_ip_selector: Int32Array

        fpu_opcode: Int32Array

        fpu_dp: Int32Array

        fpu_dp_selector: Int32Array

        reg_xmm32s: Int32Array
        mxcsr: Int32Array

        /**
         * segment registers, tr and ldtr
         */
        sreg: Uint16Array

        /**
         * Debug registers
         */
        dreg: Int32Array

        reg_pdpte: Int32Array

        svga_dirty_bitmap_min_offset: Uint32Array
        svga_dirty_bitmap_max_offset: Uint32Array

        /**
         * Firmware id value 
         */
        fw_value: Int32Array

        fw_pointer: number  

        bus: BusConnector

        do_many_cycles_count?: number
        do_many_cycles_total?: number
        seen_code?: Object
        seen_code_uncompiled?: Object

        get_state: () => Array<number|ArrayBuffer>
        set_state: (state: Array<number|ArrayBuffer>) => void
        pack_memory: () => PackedMemory
        unpack_memory: (bitmap: Uint8Array, packed_memory: Uint8Array) => void
        main_run: () => number
        reboot_internal: () => void
        reset_memory: () => void
        create_memory: (size: number) => void
        hlt_loop: () => void
        hlt_op: () => void     
    }

    export class V86 {
        /**
         * Set true when the CPU is in idle state
         */
        idle: boolean
        cpu: CPU
        running: boolean
        stopped: boolean
        tick_counter: boolean
        bus: BusConnector
        worker: Worker
    }

    /**
     * emulator instance constructor options.
     */
    export interface V86StarterOptions {
    
    /**
     * Reference to the v86 wasm exorted function.
     * @default undefined 
     */
    wasm_fn: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>

    /**
     * Path to v86 wasm artifact
     * @default "build/v86.wasm" or "build/v86-debug.wasm" when debug mode enabled 
     */
    wasm_path?: string

    /**
     * The memory size in bytes, should be a power of 2.
     * @example 16 * 1024 * 1024
     * @default 64 * 1024 * 1024
     */
    memory_size?: number

    /**
     * VGA memory size in bytes.
     * @example 8 * 1024 * 1024
     * @default 8 * 1024 * 1024
     */
    vga_memory_size?: number

    /**
     * If emulation should be started when emulator is ready.
     * @default false 
     */
    autostart?: boolean

    /**
     * If keyboard should be disabled.
     * @default false
     */
    disable_keyboard?: boolean

    /**
     * If mouse should be disabled.
     * @default false
     */
    disable_mouse?: boolean

    /**
     * If speaker should be disabled.
     * @default false
     */
    disable_speaker?: boolean

    /**
     * The url of a server running websockproxy. See [networking.md](networking.md). Setting this will enable an emulated network card.
     * @default undefined
     */
    network_relay_url?: string

    /**
     * Either a url pointing to a bios or an ArrayBuffer, see below.
     * @default undefined
     */
    bios?: V86Image

    /**
     * VGA bios, see below.
     * @default undefined
     */
    vga_bios?: V86Image

    /**
     * First hard disk, see below.
     * @default undefined
     */
    hda?: V86Image

    /** 
     * First floppy disk, see below.
     * @default undefined
     */
    fda?: V86Image

    /** 
     * cdrom
     * @default undefined
     */
    cdrom?: V86Image

    /**
     * A Linux kernel image to boot (only bzimage format)
     * @default undefined
     */
    bzimage?: V86Image

    /**
     * A Linux ramdisk image
     * @default undefined
     */
    initrd?: V86Image

    /**
     * Automatically fetch bzimage and initrd from the specified `filesystem`.
     */
    bzimage_initrd_from_filesystem?: boolean

    /**
     * An initial state to load
     * @default undefined
     */
    initial_state?: V86Image

    /**
     * A 9p filesystem
     * @default undefined
     */
    filesystem?: Filesystem9pOptions

    /** 
     * A textarea that will receive and send data to the emulated serial terminal.
     * Alternatively the serial terminal can also be accessed programatically, see [serial.html](../examples/serial.html).  
     * @default undefined
     */
    serial_container?: HTMLTextAreaElement

    /**
     * Xtermjs serial terminal container. When set, serial_container option is ignored.
     * @default undefined
     */
    serial_container_xtermjs?: HTMLElement

    /**
     * An HTMLElement. This should have a certain structure, see [basic.html](../examples/basic.html).
     * @default undefined
     */
    screen_container?: HTMLElement | null

    /**
     * ACPI
     * @default false
     */
    acpi?: boolean

    /**
     * log level
     * @default LogLevel.LOG_NONE
     */
    log_level?: LogLevel

    /**
     * boot order
     * @default BootOrder.CD_FLOPPY_HARDDISK
     */
    boot_order?: BootOrder

    /**
     * fast boot
     * @default false
     */
    fastboot?: boolean

    /**
     * enables UART1 (Serial port)
     * @see http://wiki.osdev.org/UART
     * @see https://github.com/s-macke/jor1k/blob/master/js/worker/dev/uart.js
     * @see https://www.freebsd.org/doc/en/articles/serial-uart/
     * @default undefined
     */
    uart1?: boolean

    /**
     * enables UART2 (Serial port)
     * @see http://wiki.osdev.org/UART
     * @see https://github.com/s-macke/jor1k/blob/master/js/worker/dev/uart.js
     * @see https://www.freebsd.org/doc/en/articles/serial-uart/
     * @default undefined
     */
    uart2?: boolean

    /**
     * enables UART3 (Serial port)
     * @see http://wiki.osdev.org/UART
     * @see https://github.com/s-macke/jor1k/blob/master/js/worker/dev/uart.js
     * @see https://www.freebsd.org/doc/en/articles/serial-uart/
     * @default undefined
     */
    uart3?: boolean

    /**
     * boot cmdline 
     */
    cmdline?: string

    /**
     * Ne2k: should MAC be preserved from the state image
     * @default undefined
     */
    preserve_mac_from_state_image?: boolean  

    /**
     * custom network adapter
     * @default undefined
     */
    network_adapter?: NetworkAdapter

    /**
     * enables screen dummy
     * @default undefined
     */
    screen_dummy?: boolean
    }

    export class V86Starter {
    constructor(options?: V86StarterOptions)

    /**
     * bus, use it when you must (there are a few wrappers on top of it in V86Starter that you might find helpful instead)
     */
    bus: BusConnector

    /**
     * The v86 instance.
     */
    v86: V86

    /**
     * Start emulation. Do nothing if emulator is running already. Can be asynchronous.
     */
    run(): void

    /**
     * Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
     */
    stop(): void
    destroy(): void

    /**
     * Restart (force a reboot).
     */
    restart(): void

    /**
     * Add an event listener (the emulator is an event emitter). A list of events
     * can be found at [events.md](events.md).
     *
     * The callback function gets a single argument which depends on the event.
     *
     * @param event Name of the event.
     * @param listener The callback function.
     */
    add_listener(event: string, listener: Function): void

    /**
     * Remove an event listener.
     *
     * @param event
     * @param listener
     */
    remove_listener(event: string, listener: Function): void

    /**
     * Restore the emulator state from the given state, which must be an
     * ArrayBuffer returned by
     * [`save_state`](#save_statefunctionobject-arraybuffer-callback).
     *
     * Note that the state can only be restored correctly if this constructor has
     * been created with the same options as the original instance (e.g., same disk
     * images, memory size, etc.).
     *
     * Different versions of the emulator might use a different format for the
     * state buffer.
     *
     * @param state
     */
    restore_state(state: ArrayBuffer): void

    /**
     * Asynchronously save the current state of the emulator. The first argument to
     * the callback is an Error object if something went wrong and is null
     * otherwise.
     *
     * @param callback
     */
    save_state(callback: (error: Object | null, state: ArrayBuffer) => void): void

    /**
     * Return an object with several statistics. Return value looks similar to
     * (but can be subject to change in future versions or different
     * configurations, so use defensively):
     *
     * ```javascript
     * {
     *     "cpu": {
     *         "instruction_counter": 2821610069
     *     },
     *     "hda": {
     *         "sectors_read": 95240,
     *         "sectors_written": 952,
     *         "bytes_read": 48762880,
     *         "bytes_written": 487424,
     *         "loading": false
     *     },
     *     "cdrom": {
     *         "sectors_read": 0,
     *         "sectors_written": 0,
     *         "bytes_read": 0,
     *         "bytes_written": 0,
     *         "loading": false
     *     },
     *     "mouse": {
     *         "enabled": true
     *     },
     *     "vga": {
     *         "is_graphical": true,
     *         "res_x": 800,
     *         "res_y": 600,
     *         "bpp": 32
     *     }
     * }
     * ```
     *
     * @deprecated
     */
    get_statistics(): Object
    get_instruction_counter(): number
    is_running(): boolean

    /**
     * Send a sequence of scan codes to the emulated PS2 controller. A list of
     * codes can be found at http://stanislavs.org/helppc/make_codes.html.
     * Do nothing if there is no keyboard controller.
     *
     * @param codes
     */
    keyboard_send_scancodes(codes: number[]): void
    
    /**
     * Send translated keys
     */
    keyboard_send_keys(codes: any[]): void

    /**
     * Send text
     */
    keyboard_send_text(string: string): void

    
    /**
     * Download a screenshot.
     */
    screen_make_screenshot(): void

    /**
     * Set the scaling level of the emulated screen.
     *
     * @param {number} sx
     * @param {number} sy
     *
     * @ignore
     * @export
     */
    screen_set_scale(sx: number, sy: number): void

    /**
     * Go fullscreen.
     */
    screen_go_fullscreen(): void

    /**
     * Lock the mouse cursor: It becomes invisble and is not moved out of the browser window.
     */
    lock_mouse(): void

    /**
     * Enable or disable sending mouse events to the emulated PS2 controller.
     */
    mouse_set_status(enabled: boolean): void
    
    /**
     * Enable or disable sending keyboard events to the emulated PS2 controller.
     */
    keyboard_set_status(enabled: boolean): void

    /**
     * Send a string to the first emulated serial terminal.
     *
     * @param data
     */
    serial0_send(data: string): void

    /**
     * Send bytes to a serial port (to be received by the emulated PC).
     *
     * @param serial the index of the serial port
     * @param data
     */
    serial_send_bytes(serial: number, data: Uint8Array): void

    /**
     * Mount another filesystem to the current filesystem.
     * @param path Path for the mount point
     * @param baseurl
     * @param basefs As a JSON string
     * @param callback
     * @export
     */
    mount_fs(path: string, baseurl?: string, basefs?: string, callback?: (error: Object | null) => void): void

    /**
     * Write to a file in the 9p filesystem. Nothing happens if no filesystem has
     * been initialized. First argument to the callback is an error object if
     * something went wrong and null otherwise.
     *
     * @param file
     * @param data
     * @param callback
     */
    create_file(file: string, data: Uint8Array, callback?: (error: Object | null) => void): void

    /**
     * Runs a set of automatic steps
     * @param steps
     */
    automatically(steps: V86AutomaticStep[]): void

    /**
     * Reads data from memory at specified offset.
     *
     * @param offset
     * @param length
     */
    read_memory(offset: number, length: number): Array<number> | Uint8Array

    /**
     * Writes data to memory at specified offset.
     *
     * @param {Array.<number>|Uint8Array} blob
     * @param {number} offset
     */
    write_memory(blob: Array<number> | Uint8Array, offset: number): void
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants