From db1c9aeaebcd13bf1d2cdc99212a519346112b9d Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 21:21:56 -0700 Subject: [PATCH 1/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 210344b..dab3262 100644 --- a/README.md +++ b/README.md @@ -392,7 +392,7 @@ The Apple II support covers the full range of the Apple II family. From the Rev Probably the most exciting development is the support for the Apple ///. PLASMA on the Apple /// provides 32K for global data and 6502 code, and the rest of the memory for bytecode and extended data. ## References -PLASMA User Manual: https://github.com/dschmenk/PLASMA/blob/master/User%20Manual.md +PLASMA User Manual: https://github.com/dschmenk/PLASMA/blob/master/doc/User%20Manual.md B Programming Language User Manual http://cm.bell-labs.com/cm/cs/who/dmr/kbman.html From 921c6115674c2e7808daaf3e366834ad55b5e00f Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 21:26:41 -0700 Subject: [PATCH 2/8] Create Architecture --- doc/Architecture | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 doc/Architecture diff --git a/doc/Architecture b/doc/Architecture new file mode 100644 index 0000000..2214a92 --- /dev/null +++ b/doc/Architecture @@ -0,0 +1,2 @@ +# PLASMA 123 Architecture +This document describes the low-level implementation of PLASMA. It is not necessary to know how PLASMA is implemented to write PLASMA programs, but understanding how the virtual machine operates can give you insight on how certain operations are carried out and how to write optimal PLASMA code. It *is* a requirement to understand when interfacing to native 6502 code. From ef98c5c878d59094f748259ba9618201947d07a6 Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 21:27:04 -0700 Subject: [PATCH 3/8] Rename Architecture to Architecture.md --- doc/{Architecture => Architecture.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{Architecture => Architecture.md} (100%) diff --git a/doc/Architecture b/doc/Architecture.md similarity index 100% rename from doc/Architecture rename to doc/Architecture.md From 724c600d182343f316fa2452f1b7b98345951c1e Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 21:30:01 -0700 Subject: [PATCH 4/8] Update User Manual.md --- doc/User Manual.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/User Manual.md b/doc/User Manual.md index 64a6c43..9240c3c 100644 --- a/doc/User Manual.md +++ b/doc/User Manual.md @@ -1,5 +1,5 @@ -# PLASMA Programming User Manual -## ( Proto Language AsSeMbler for Apple) +# PLASMA 123 Programming User Manual +## (Proto Language AsSeMbler for Apple) ## Introduction PLASMA is a medium level programming language targetting the 8 bit 6502 processor. Historically, there were simple languages developed in the early history of computers that improved on the tedium of assembly language programming while still being low level enough for system coding. Languages like B, FORTH, and PLASMA fall into this category. The following will take you through the process of writing, building and running a PLASMA module. From 99ee9bb1cf9163586f11c954ef9dada3a7e96454 Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 22:25:52 -0700 Subject: [PATCH 5/8] Update Architecture.md --- doc/Architecture.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/Architecture.md b/doc/Architecture.md index 2214a92..a27fb84 100644 --- a/doc/Architecture.md +++ b/doc/Architecture.md @@ -1,2 +1,17 @@ -# PLASMA 123 Architecture -This document describes the low-level implementation of PLASMA. It is not necessary to know how PLASMA is implemented to write PLASMA programs, but understanding how the virtual machine operates can give you insight on how certain operations are carried out and how to write optimal PLASMA code. It *is* a requirement to understand when interfacing to native 6502 code. +# PLASMA 123 Internal Architecture +This document describes the low-level implementation of PLASMA. It is not necessary to know how PLASMA is implemented to write PLASMA programs, but understanding how the virtual machine operates can give you insight on how certain operations are carried out and how to write optimal PLASMA code. It *is* a requirement to understand when interfacing to native 6502 code. PLASMA consists of a virtual machine and a compiler to translate PLASMA source code to PLASMA bytecode. + +## The Virtual Machine +The 6502 processor is a challenging target for a compiler. Most high level languages do have a compiler avialable targetting the 6502, but none are particularly efficient at code generation. Usually a series of calls into routines that do much of the work, not too dissimlar to a threaded interpreter. Generating inline 6502 leads quickly to code bloat and unwieldy binaries. The trick is to find a happy medium between efficient code execution and small code size. To this end, the PLASMA VM enforces some restrictions that are a result of the 6502 architecture, yet don't hamper the expressiveness of the PLASMA language. + +### The Stacks +The PLASMA VM is architected around three stacks: the evaluation stack, the call stack, and the local frame stack. These stacks provide the PLASMA VM with foundation for efficient operation and compact bytecode. The stack architecure also creates a simple target for the PLASMA compiler. + +#### The Evaluation Stack +All calculations, data moves, and paramter passing is done on the evaluation stack. This stack is located on the zero page of the 6502; an efficient section of memory that can be addressed with only an eight bit address. As a structure that is accessed more than any other on PLASMA, it makes sense to put it in fastest memory. The evaluation stack is a 16 entry stack that is split into low bytes and high bytes. The 6502's X register is used to index into the evaluation stack. It *always* points to the top of the evaluation stack, so care must be taken to save/restore its value when calling native 6502 code. Parameters and results are also passed on the evaluation stack. Caller and callee must agree on the number of parameters: PLASMA does no error checking. Native functions can pull values from the evaluation stack by using the zero page indexed addressing using the X register. + +#### The Call Stack +Function calls use the call stack to save the return address of the calling code. PLASMA uses the 6502 hardware stack for this purpose, as it is the 6502's JSR (Jump SubRoutine) instruction that PLASMA's call opcodes are implemented. + +#### The Local Frame Stack +One of the biggest problems to overcome with the 6502 is its very small hardware stack. Algorithms that incorporate recursive procedure calls are very difficult or slow on the 6502. PLASMA takes the middle ground when implementing local frames; a frame pointer on the zero page is indirectly indexed by the Y register. Because the Y register is only eight bits, the local frame size is limited to 256 bytes. 256 bytes really is sufficient for all but the most complex of functions. With a little creative use of dynamic memory allocation, almost anything can be implemented without undue hassle. When a function with parameters is called, the first order of business is to allocate the frame, copy the parameters off the evaluation stack into local variables, and save a link to the previous frame. This is all done automatically with the ENTER opcode. The reverse takes place with the LEAVE opcode when the function exits. Functions that have neither parameters or local variables can forgoe the frame build/destroy process. From 8c967e6fe03b124605a963a89347284a4d4cec2d Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 22:36:06 -0700 Subject: [PATCH 6/8] Update Architecture.md --- doc/Architecture.md | 71 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/doc/Architecture.md b/doc/Architecture.md index a27fb84..644b17c 100644 --- a/doc/Architecture.md +++ b/doc/Architecture.md @@ -15,3 +15,74 @@ Function calls use the call stack to save the return address of the calling code #### The Local Frame Stack One of the biggest problems to overcome with the 6502 is its very small hardware stack. Algorithms that incorporate recursive procedure calls are very difficult or slow on the 6502. PLASMA takes the middle ground when implementing local frames; a frame pointer on the zero page is indirectly indexed by the Y register. Because the Y register is only eight bits, the local frame size is limited to 256 bytes. 256 bytes really is sufficient for all but the most complex of functions. With a little creative use of dynamic memory allocation, almost anything can be implemented without undue hassle. When a function with parameters is called, the first order of business is to allocate the frame, copy the parameters off the evaluation stack into local variables, and save a link to the previous frame. This is all done automatically with the ENTER opcode. The reverse takes place with the LEAVE opcode when the function exits. Functions that have neither parameters or local variables can forgoe the frame build/destroy process. + +### The Byecodes +The compact code representation comes through the use of opcodes closely matched to the PLASMA compiler. They are: + +| OPCODE | Description +|:------:|----------------------------------- +| ZERO | push zero on the stack +| ADD | add top two values, leave result on top +| SUB | subtract next from top from top, leave result on top +| MUL | multiply two topmost stack values, leave result on top +| DIV | divide next from top by top, leave result on top +| MOD | divide next from top by top, leave remainder on top +| INCR | increment top of stack +| DECR | decrement top of stack +| NEG | negate top of stack +| COMP | compliment top of stack +| AND | bit wise AND top two values, leave result on top +| IOR | bit wise inclusive OR top two values, leave result on top +| XOR | bit wise exclusive OR top two values, leave result on top +| LOR | logical OR top two values, leave result on top +| LAND | logical AND top two values, leave result on top +| SHL | shift left next from top by top, leave result on top +| SHR | shift right next from top by top, leave result on top +| IDXB | add top of stack to next from top, leave result on top (ADD) +| IDXW | add 2X top of stack to next from top, leave result on top +| NOT | logical NOT of top of stack +| LA | load address +| LLA | load local address from frame offset +| CB | constant byte +| CW | constant word +| SWAP | swap two topmost stack values +| DROP | drop top stack value +| DUP | duplicate top stack value +| PUSH | push top to call stack +| PULL | pull from call stack +| BRGT | branch next from top greater than top +| BRLT | branch next from top less than top +| BREQ | branch next from top equal to top +| BRNE | branch next from top not equal to top +| ISEQ | if next from top is equal to top, set top true +| ISNE | if next from top is not equal to top, set top true +| ISGT | if next from top is greater than top, set top true +| ISLT | if next from top is less than top, set top true +| ISGE | if next from top is greater than or equal to top, set top true +| ISLE | if next from top is less than or equal to top, set top true +| BRFLS | branch if top of stack is zero +| BRTRU | branch if top of stack is non-zero +| BRNCH | branch to address +| CALL | sub routine call with stack parameters +| ICAL | sub routine call to indirect address on stack top with stack parameters +| ENTER | allocate frame size and copy stack parameters to local frame +| LEAVE | deallocate frame and return from sub routine call +| RET | return from sub routine call +| LB | load byte from top of stack address +| LW | load word from top of stack address +| LLB | load byte from frame offset +| LLW | load word from frame offset +| LAB | load byte from absolute address +| LAW | load word from absolute address +| SB | store top of stack byte into next from top address +| SW | store top of stack word into next from top address +| SLB | store top of stack into local byte at frame offset +| SLW | store top of stack into local word at frame offset +| SAB | store top of stack into byte at absolute address +| SAW | store top of stack into word at absolute address +| DLB | duplicate top of stack into local byte at frame offset +| DLW | duplicate top of stack into local word at frame offset +| DAB | duplicate top of stack into byte at absolute address +| DAW | duplicate top of stack into word at absolute address + +The opcodes were developed over time by starting with a very basic set of operations and slowly adding opcodes when the PLASMA compiler could improve code density or performance. From d9a2298c7619103739e85b63d1d6a0bc72e5384c Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 22:36:24 -0700 Subject: [PATCH 7/8] Update Architecture.md --- doc/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Architecture.md b/doc/Architecture.md index 644b17c..2f1183f 100644 --- a/doc/Architecture.md +++ b/doc/Architecture.md @@ -16,7 +16,7 @@ Function calls use the call stack to save the return address of the calling code #### The Local Frame Stack One of the biggest problems to overcome with the 6502 is its very small hardware stack. Algorithms that incorporate recursive procedure calls are very difficult or slow on the 6502. PLASMA takes the middle ground when implementing local frames; a frame pointer on the zero page is indirectly indexed by the Y register. Because the Y register is only eight bits, the local frame size is limited to 256 bytes. 256 bytes really is sufficient for all but the most complex of functions. With a little creative use of dynamic memory allocation, almost anything can be implemented without undue hassle. When a function with parameters is called, the first order of business is to allocate the frame, copy the parameters off the evaluation stack into local variables, and save a link to the previous frame. This is all done automatically with the ENTER opcode. The reverse takes place with the LEAVE opcode when the function exits. Functions that have neither parameters or local variables can forgoe the frame build/destroy process. -### The Byecodes +### The Bytecodes The compact code representation comes through the use of opcodes closely matched to the PLASMA compiler. They are: | OPCODE | Description From d23dba2cc2da970d69d89320107ef436e92ce8ed Mon Sep 17 00:00:00 2001 From: David Schmenk Date: Thu, 12 Jun 2014 23:01:34 -0700 Subject: [PATCH 8/8] Update Architecture.md --- doc/Architecture.md | 131 ++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/doc/Architecture.md b/doc/Architecture.md index 2f1183f..f44e73d 100644 --- a/doc/Architecture.md +++ b/doc/Architecture.md @@ -19,70 +19,71 @@ One of the biggest problems to overcome with the 6502 is its very small hardware ### The Bytecodes The compact code representation comes through the use of opcodes closely matched to the PLASMA compiler. They are: -| OPCODE | Description -|:------:|----------------------------------- -| ZERO | push zero on the stack -| ADD | add top two values, leave result on top -| SUB | subtract next from top from top, leave result on top -| MUL | multiply two topmost stack values, leave result on top -| DIV | divide next from top by top, leave result on top -| MOD | divide next from top by top, leave remainder on top -| INCR | increment top of stack -| DECR | decrement top of stack -| NEG | negate top of stack -| COMP | compliment top of stack -| AND | bit wise AND top two values, leave result on top -| IOR | bit wise inclusive OR top two values, leave result on top -| XOR | bit wise exclusive OR top two values, leave result on top -| LOR | logical OR top two values, leave result on top -| LAND | logical AND top two values, leave result on top -| SHL | shift left next from top by top, leave result on top -| SHR | shift right next from top by top, leave result on top -| IDXB | add top of stack to next from top, leave result on top (ADD) -| IDXW | add 2X top of stack to next from top, leave result on top -| NOT | logical NOT of top of stack -| LA | load address -| LLA | load local address from frame offset -| CB | constant byte -| CW | constant word -| SWAP | swap two topmost stack values -| DROP | drop top stack value -| DUP | duplicate top stack value -| PUSH | push top to call stack -| PULL | pull from call stack -| BRGT | branch next from top greater than top -| BRLT | branch next from top less than top -| BREQ | branch next from top equal to top -| BRNE | branch next from top not equal to top -| ISEQ | if next from top is equal to top, set top true -| ISNE | if next from top is not equal to top, set top true -| ISGT | if next from top is greater than top, set top true -| ISLT | if next from top is less than top, set top true -| ISGE | if next from top is greater than or equal to top, set top true -| ISLE | if next from top is less than or equal to top, set top true -| BRFLS | branch if top of stack is zero -| BRTRU | branch if top of stack is non-zero -| BRNCH | branch to address -| CALL | sub routine call with stack parameters -| ICAL | sub routine call to indirect address on stack top with stack parameters -| ENTER | allocate frame size and copy stack parameters to local frame -| LEAVE | deallocate frame and return from sub routine call -| RET | return from sub routine call -| LB | load byte from top of stack address -| LW | load word from top of stack address -| LLB | load byte from frame offset -| LLW | load word from frame offset -| LAB | load byte from absolute address -| LAW | load word from absolute address -| SB | store top of stack byte into next from top address -| SW | store top of stack word into next from top address -| SLB | store top of stack into local byte at frame offset -| SLW | store top of stack into local word at frame offset -| SAB | store top of stack into byte at absolute address -| SAW | store top of stack into word at absolute address -| DLB | duplicate top of stack into local byte at frame offset -| DLW | duplicate top of stack into local word at frame offset -| DAB | duplicate top of stack into byte at absolute address -| DAW | duplicate top of stack into word at absolute address +| OPCODE | Name | Description +|:------:|:------:|----------------------------------- +| $00 | ZERO | push zero on the stack +| $02 | ADD | add top two values, leave result on top +| $04 | SUB | subtract next from top from top, leave result on top +| $06 | MUL | multiply two topmost stack values, leave result on top +| $08 | DIV | divide next from top by top, leave result on top +| $0A | MOD | divide next from top by top, leave remainder on top +| $0C | INCR | increment top of stack +| $0E | DECR | decrement top of stack +| $10 | NEG | negate top of stack +| $12 | COMP | compliment top of stack +| $14 | AND | bit wise AND top two values, leave result on top +| $16 | IOR | bit wise inclusive OR top two values, leave result on top +| $18 | XOR | bit wise exclusive OR top two values, leave result on top +| $1A | SHL | shift left next from top by top, leave result on top +| $1C | SHR | shift right next from top by top, leave result on top +| $02 | IDXB | add top of stack to next from top, leave result on top (ADD) +| $1E | IDXW | add 2X top of stack to next from top, leave result on top +| $20 | NOT | logical NOT of top of stack +| $22 | LOR | logical OR top two values, leave result on top +| $24 | LAND | logical AND top two values, leave result on top +| $26 | LA | load address +| $28 | LLA | load local address from frame offset +| $2A | CB | constant byte +| $2C | CW | constant word +| $2E | SWAP | swap two topmost stack values +| $30 | DROP | drop top stack value +| $32 | DUP | duplicate top stack value +| $34 | PUSH | push top to call stack +| $36 | PULL | pull from call stack +| $38 | BRGT | branch next from top greater than top +| $3A | BRLT | branch next from top less than top +| $3C | BREQ | branch next from top equal to top +| $3E | BRNE | branch next from top not equal to top +| $40 | ISEQ | if next from top is equal to top, set top true +| $42 | ISNE | if next from top is not equal to top, set top true +| $44 | ISGT | if next from top is greater than top, set top true +| $46 | ISLT | if next from top is less than top, set top true +| $48 | ISGE | if next from top is greater than or equal to top, set top true +| $4A | ISLE | if next from top is less than or equal to top, set top true +| $4C | BRFLS | branch if top of stack is zero +| $4E | BRTRU | branch if top of stack is non-zero +| $50 | BRNCH | branch to address +| $52 | IBRNCH | branch to address on stack top +| $54 | CALL | sub routine call with stack parameters +| $56 | ICAL | sub routine call to address on stack top with stack parameters +| $58 | ENTER | allocate frame size and copy stack parameters to local frame +| $5A | LEAVE | deallocate frame and return from sub routine call +| $5C | RET | return from sub routine call +| $60 | LB | load byte from top of stack address +| $62 | LW | load word from top of stack address +| $64 | LLB | load byte from frame offset +| $66 | LLW | load word from frame offset +| $68 | LAB | load byte from absolute address +| $6A | LAW | load word from absolute address +| $6C | DLB | duplicate top of stack into local byte at frame offset +| $6E | DLW | duplicate top of stack into local word at frame offset +| $70 | SB | store top of stack byte into next from top address +| $72 | SW | store top of stack word into next from top address +| $74 | SLB | store top of stack into local byte at frame offset +| $76 | SLW | store top of stack into local word at frame offset +| $78 | SAB | store top of stack into byte at absolute address +| $7A | SAW | store top of stack into word at absolute address +| $7C | DAB | duplicate top of stack into byte at absolute address +| $7E | DAW | duplicate top of stack into word at absolute address The opcodes were developed over time by starting with a very basic set of operations and slowly adding opcodes when the PLASMA compiler could improve code density or performance.