General Category > Programming

JSASM - JS virtual assembler

(1/2) > >>

I've been playing the idea of developing in a virtual machine in a virtual assembly language - I have even coded a pretty reasonable POC.

It works by coding your assembly language in a hybrid JS / Assembly language and you can run and debug it in your browser - no more crashing or annoying inside emulator development.  The goal is to convert the virtual assembler to native assembler - which is a lot easier than a full blown compiler and we get better resulting code.

Any thoughts?


There are a set of virtual registers which would map to real registers as closely as possible.  Basically A8 to E8 for 8bit ones and A16 to E16 for 16 bit ones.  Using the registers closest to A are more likely to be allocated to real registers than ones below depending on target platform.

--- Code: ---// registers
// js            6502                  z80
// ==         ====               ===
// A8, A8'         A                  A
// B8, B8'         X                  B
// C8, C8'         Y                  C
// D8, D8'         simulated               simulated
// E8, E8'         simulated               simulated

// A16, A16'      simulated               HL
// B16, B16'      simulated               DE
// C16, C16'      simulated               IX
// D16, D16'      simulated               IY
// E16, E16'      simulated               simulated

// F,  F'         S                  F
// M, M'         virtual               virtual
// PC            PC                  PC
// SP            simulated               SP

// flags
// js            6502                  z80
// ==         ====               ===
// C            c                  c
// I            i                  simulated
// Z            z                  z

--- End code ---

Instruction Summary:

--- Code: ---
// instruction summary
// valid statuses: R = Simulator, 6 = 6502, Z = Z80
// status         vasm            6502                  z80
// ======         ====            ====                  ===

// (general register movement instructions)

// R            alt               simulated               exx
// R            ld                simulated               ld
//                              lda,ldx,ldy   
//                              sta,stx,sty
//                              tax, txa
//                              tay, tya
//                              tsx, txs
// R            nop               nop                     nop
// R            pop               pla,plp                  pop
// R            push            pha,php                  push
// R            swap            simulated               ex & simulated

// (flow control instructions)

// R            call            jsr                     call
// R            if(test(         simulated               simulated
// R            return            ret                     ret
// R            while(test(         simulated               simulated

// (arithmetic instructions)

// R            adc               adc                     adc
// R            add               ?                     add
// R            cp               cmp,cpx,cpy               cp
// R            dec               dex,dey                  dec
// PRIORITY2      decm            dec                     simulated: ld hl,m; dec (hl)
// R            div               simulated               simulated
// R            inc               inx,iny                  inc
// PRIORITY2      incm            inc                     simulated: ld hl,m; inc (hl)
// R            mod               simulated               simulated
// R            mult            simulated               simulated
// R            sbc               sbc                     sbc
// R            sub               ?                     sub

// (input / output instructions)

//   PRIORITY2      input            simulated               in
//   R            output            simulated               out

// (bit instructions)

// PRIORITY1      and               and                     and
// PRIORITY2      bit               bit                     bit
// PRIORITY1      or               ora                     or
// PRIORITY3      rla               rol                     rla
// PRIORITY3      rra               ror                     rra
// R            set(C            clc                     simulated: and a
// R            set(I            sei                     di, ei
// R            set(Z            simulated               simulated
// PRIORITY3      sla               asl                     sla
// PRIORITY3      sra               lsr                     sra
// PRIORITY1      xor               eor                     xor

// (block instructions)

// PRIORITY4      cpd               simulated               cpd
// PRIORITY4      cpdr            simulated               cpdr
// PRIORITY4      cpi               simulated               cpi
// PRIORITY4      cpir            simulated               cpir

// PRIORITY4      ldd               simulated               ldd
// PRIORITY4      lddr            simulated               lddr
// PRIORITY4      ldi               simulated               ldi
// PRIORITY4      ldir            simulated               ldir

// replaced instructions: by while(test(,  if(test(  and return

//                jp               jmp                     jp
//                jpnc            bcc                     jp nc
//                jpc               bcs                     jp c
//                jrnc            bcc                     jr nc
//                jrc               bcs                     jr c
//                jpnz            bne                     jp nz
//                jpz               beq                     jp z
//                jrnz            bne                     jr nz
//                jrz               beq                     jr z
//                reti            rti                     reti

--- End code ---



Working JavaScript, within the simulator at the moment I have setup output port 8 to be output a char to the console.  It is quite obvious on a z80, what the below would assemble to.

--- Code: ---
function main()
   ld(A8, 5);
   set(Z, 0);


function outA8()
   output(1, A8);


--- End code ---



There have been only a couple of challenges so far but I think I have reasonably been able to get through them. 

Addressing Modes:

Coming up with a method of implementing addressing modes in JS which translates optimally to both a z80 and 6502, but I am pretty happy with the result so far.  That is as you would usually expect, but we have 3 addressing modifiers: M for memory, H for high byte and L for low byte e.g. 

Summary of ld commands:

--- Code: ---
// A:r8 -v ld(<R8>, <value>); ld a, 100
// B:r8 -r8 ld(<R8>, <R8>); ld a, b
// C:r8 -mv ld(<R8>, M(<value>)); ld a, (100)
// D:r8 -mr16 ld(<R8>, M(<R16>)); ld a, (hl)
// E:r8 -l ld(<R8>, L(<R16>)); ld a, l
// F:r8 -h ld(<R8>, H(<R16>)); ld a, h

// G:r16-v ld(<R16>, <value>); ld hl, 100
// H:r16-f ld(<R16>, <function>); ld hl, function1
// I:r16-r16 ld(<R16>, <R16>); ld hl, de
// J:r16-mv ld(<R16>, M(<value>)); ld hl, (100)
// K:r16-mr16 ld(<R16>, M(<R16>)); ld hl, (de)

// L:mv -r8 ld(M(<value>), <R8>); ld (100), a
// M:mv -r16 ld(M(<value>), <R16>); ld (100), hl
// N:mv -l ld(M(<value>), L(<R16>)); ld (100), l
// O:mv -h ld(M(<value>), H(<R16>)); ld (100), h

// P:mr16-r8 ld(M(<R16>), <R8>); ld (hl), a
// Q:mr16-r16 ld(M(<R16>), <R16>); ld (de), hl
// R:mr16-l ld(M(<R16>), L(<R16>)); ld (de), l
// S:mr16-h ld(M(<R16>), H(<R16>)); ld (de), h

// T:l -v ld(L(<R16>), <value>); ld l, 100
// U:l -r8 ld(L(<R16>), <R8>); ld l, a
// V:l -mv ld(L(<R16>), M(<value>)); ld l, (100)
// W:l -mr16 ld(L(<R16>), M(<R16>)); ld l, (de)

// X:h -v ld(H(<R16>), <value>); ld h, 100
// Y:h -r8 ld(H(<R16>), <R8>); ld h, a
// Z:h -mv ld(H(<R16>), M(<value>)); ld h, (100)
// 0:h -mr16 ld(H(<R16>), M(<R16>)); ld h, (de)

--- End code ---


Stack Operations:

The only thing I have found a solution which I would rather a 'better' solution is the fact that a z80 only has 16bit pushes/pops and a 6502 only has only 8bit pushes/pops - meaning a bit of trickery to make them behave the same - even if simulating on real hardware.  Because a Z80 pushes A and F together, restoring eg: F would also restore A even if not wanted.

We currently allow:

push(A8); and push(A16); and their pop alternatives pop(A8); and pop(A16);


[0] Message Index

[#] Next page

Go to full version
Powered by SMFPacks Reactions Mod
Powered by SMFPacks Alerts Pro Mod
Powered by SMFPacks Mentions Pro Mod