Initial import

This commit is contained in:
David Schmenk 2018-04-25 09:13:44 -07:00
commit 1066a69950
258 changed files with 116171 additions and 0 deletions

13
BUILD.TXT Executable file
View File

@ -0,0 +1,13 @@
This is the initial build process based on my very crusty and horrible makefile. This should improve as help arrives.
1. Download/build/install cc65
2. Install Java developers kit
3. Check out vm02 source
4. 'cd vm02/src'
5. 'make clean'
6. 'make'
7. 'make image'
This will put the binary files into the vm02/release-$(VERSION) and vm02/samples-$(VERSION) directories in Virtual ][ name format. You can use something like CiderPress to copy the files into a disk image if you don't use Virtual ][.
Dave...

317
DESIGN.TXT Executable file
View File

@ -0,0 +1,317 @@
VM02 V1.0 Design
================
Author: Dave Schmenk
Date: 4/2/2010
Revision: 1.3
Introduction
============
VM02 is a Java compatible VM written in 6502 assembly language for the Apple II series of computers. Everything has been implemented from scratch using the JVM specification and black-boxing the Sun JVM with sample code. Certian aspects of the JVM have been simplified or removed to fit the target machine. This document is an attempt to record the design of VM02 for other developers to come up to speed, as well as to jog the memory of the original developer who has a tendency to forget what he has done.
VM02 has quite a diversity of technology. Some is rather sophisticated and not deeply discussed. Floating point operations, DVM implementation, IRQ support, and other details are left out, but a perusal of the source should answer most questions.
The design document will follow the implementation of VM02 as that was how many decisions were made along the way. Starting with the memory manager and class file loader which drives much of the following implementation through the class database, frame manager, thread scheduler, device drivers, and ending with DVM, the hope is that many questions can be answered by reading through this document.
Before delving into the design, a little history is in order. The genesis for VM02 starts way back with the development of Apple Pascal around 1978. UCSD Pascal was brought to the Apple II and became one of the most powerful development environments for the fledging microcomputer market. Indeed, Apple Pascal was the first real computer language I learned in 1979 (after toying with with a TRS-80 Model 1 in school). Many years later, the retro-computing bug hit and I wanted to bring a modern, up-to-date programming environment to my favorite computer. After thinking about developing my own custom VM and language, a post on comp.sys.apple2 from Oliver Schmidt about porting a very limited Java-like VM to the Apple II got me thinking. Oliver's experiment was a success in that he got it working, but it was much too slow and cumbersome to be really useful. I decided then that a real JVM was possible and the performance issues could be solved with a clever assembly language implementation. The beginning of VM02 involved deciding what could be feasibly incorporated into a VM and what had to be left out. Ultimately I picked 64 bit data types as the cut-off for functionality. Everything else should be implemented in some fashion. Lastly, a JVM is only part of the equation when it comes to supporting a language like Java. Most of the language involves a huge library of classes that provide the functionality to be useful. I have attempted to write the bare minimum of classes to make the Apple II a viable Java target.
External Linkeage (global.inc)
------------------------------
Page 3 ($0300-$03EF) is used for external entrypoints into VM02. Since the 6502 doesn't support indirect subroutine calls, the values have to be copied to a suitable location for calling. DVM has an indirect call opcode to simplify calling VM02 from native methods. Device drivers are accessed through function pointers as well as a few choice variables for global access. These links will remain stable for all of version 1.
Initiallization (vm02.s thread.s)
---------------------------------
Initialization is a process to get every module of VM02 ready to execute the main class. All module's init code is called, then the inital class is searched for. Either a command string can be passed in from a previous program, the existance of a STARTUP class, or the user is prompted for an initial class to execute. The command string or command line is then parsed for parameters and installed into and argument string. An initial thread is created and the name of the main class is pushed on the stack for the thread to load and call. The thread is then started. Execution returns to VM_EXIT after all threads have finished. VM02 then reloads itself and begins the search again. Command strings and return values can be passed from one class to the next to create an exec chain.
Memory Management (memmgr.s fames.s ops.s sysclass.s)
-----------------------------------------------------
Even before the decision to implement a JVM, a memory manager had been worked out to provide a handle-based manager for a VM. The reason was to allow for ease of garbage collection, memory flexibility, and eventually memory swapping to disk. Memory blocks can be allocated, freed, moved, locked, and unlocked. The memory handle table is defined at the top of main memory, right below ProDOS, in the 64K configuration. The 128K VM places the handle table in the second bank of the AUX language card area. The number of memory handles is a define in global.inc. In the 64K configuration, the memory table takes up space in main memory so there is a tradeoff between the size of the table and free main memory.
A memory handle is an entry in the handle table that points to the actual memory block, plus some flags in the low-order 3 bits. These bits are masked off when converting to an address. It also means that the memory blocks are multiples of 8 bytes. An allocated memory block has a 4 byte header containing the size of the block, the reference count of the block, and an accessed flag. A free block contains the size of the block and a handle to the next free block in the free list. The free list header is located in zero page. The smallest available memory block is thus 4 bytes (8 byte block minimum - 4 header bytes). It makes sense to try and allocate blocks that are as large as possible to avoid wasting memory and handles, thus causing fragmentation.
Memory blocks can be allocated, freed, locked, unlocked, and reference counted. Memory can be optionally allocated at fixed addresses to support mapping the framebuffer or fullfilling the alignment requirements of ProDOS and other hardware/software. Memory can be code or data. In the 128K configuration, bytecode is actually stored in AUX memory, but the details are retained in a main memory block. This gives the system enough flexibility to manage memory in a very constrained environment.
In the apple2/ProDOS.java class is a method: ioAllocBuffer() which calls an internal VM02 routine to allocate a fixed memory block. It actually scans from the bottom of memory to the top until it succeeds in allocating the block. If it fails, it forces GC and tries again. It will fail eventually, so it is best to allocate the IO buffer early on in the program to guarantee getting it. The HGR2 buffer is allocated using the same call in apple2/AppleStuff.java, although it fails immediately if it can't get the memory. VM02 uses it's own buffers for class loading and swapping that are independent of the Java class's file I/O. All operate independently. Java threads should use the Java synchronization features if doing I/O to the same file, otherwise they too are independent. The number of files open is only limited by available memory and ProDOS.
Reference count incrementing is handled throught the VM, but reference decrementing is only handled through the object unreference routine (UNREF_OBJ) in sysclass.s. Whenever a new value is written into a variable or a local method frame is destroyed, reference counts to the current values are decremented. If the reference count goes to zero, then any objects made reference to by the object to be freed must also be decremented. It can get quite recursive. The finalize() method on any object to be freed is called immediately. This way memory can be returned to the pool as quickly as possible. Background memory collection is expensive both in time and space; VM02 has neither. Reference counting goes hand-in-hand with garbage collecting.
Garbage collection is discussed in more detail later, but briefly it combines adjacent free space and shifts free memory down in memory. The concept is to keep allocated memory blocks as high up in memory as possible. One reason is that the VM itself exists in low memory, right below the second hi-res page. Keeping low memory free makes usage of the hi-res graphics more possible. There is a routine called from the scheduler and any time free memory is exhausted to first combine any adjacent free space, the move and allocated blocks up in memory. This process is repeated a given number of times. If called from the memory allocator and it is unable to satisfy the request, an out-of-memory error will be thrown. These algorithms can be combined with a build-optional demand swapping feature described next.
Demand memory swapping to disk is supported through the use of a SWAP directory. Handles are converted to filenames and the data is read/written from/to the file. Accessed tags are incrementally cleared through the THREAD_YIELD routine. The memory allocator will now call two swapping routines when RAM is exhausted. The first will swap out all unaccessed blocks, the other will aggressively swap out all unlocked blocks. The idle loop garbage collector will combine any free blocks, try to move free space to lower memory, and write out any blocks which have been unaccessed for some time. Swap volumes are intelligently determined at init time. RAM disks of sufficient size have precedence over hard media. Finally, the SWAP directory is cleaned up at exit time.
Strings (strpools.s)
--------------------
Strings in Jave are immutable - they don't change. Whenever a change is made, a new String object is created. VM02 works in concert with the memory manager to reference count strings. Only one copy of a string is kept. In VM02, if two strings are the same, their object references are the same too. Only strings of 255 charcters or less are permitted, and they are only 8 bit characters (UTF8), so no unicode support. Strings are hashed and searched through a linked list per hash table entry. Efficient searching, adding, and deleting of strings is important.
Class File Loader (classload.s class.inc)
-----------------------------------------
Class file loading is one of the most fundamental operations of VM02. One of the most important requirements for VM02 was that it use the well defined Java Class File Format. Many other small JVM implementations remove this requirement and create their own file format that eases the class field and offset resolution operations. Luckily, ProDOS provides a fairly sophisticated file system for directory and file management.
The Java Class File Format is available on-line and should be reviewed to understand the internals of VM02. It is the basis for the single inheritence model of Java, as well as the exception and bytecode definitions. The class file maps the Java class into a binary representation, similar to a .o file in UNIX, or a .OBJ in MS-DOS/Windows. Linking occurs at runtime, and can be either lazy or immediate. VM02 chooses the lazy linking method as it only allocates resources for a class when it is called. This keeps the memory footprint as small as possible but can pause a running program to load referenced classes. The class file has four major sections: the constant pool, the interfaces, the fields, and the methods. Due to the way the class file is laid out, the size of the sections isn't known until each section is read in. This creates a little difficulty in allocating a data structure to contain the full class. Instead, the class loader allocates a data structure big enough to contain counts and handles for the interface descriptors, field descriptors, and method descriptors as well as the constant pool itself. Each constant pool entry is 5 bytes: one type bye and 4 data bytes. In the class file, they are dynamic in size, but to make the offset calculation easy, they are fixed in the class data structure. All the other sections are read in order, allocating a memory block to hold each section's descriptors. Method sections also call a code loader to install the method's code into the code manager (codemgr.s). After all the sections have been successfully loaded, a check is made to see if the superclass has beenn loaded. If not, the class loader is recursively called to load the superclass. Once all superclasses are available, the fields and methods are fixed up based on the superclass. This is probably the hardest code in all of VM02 to follow. This is where the fundamentals of the inheritance model come in to play. Fields shadow any superclass fields. Static methods shadow any superclass methods, whereas virtual methods have to override any superclass virtual methods in a virtual method table. This is not pretty code to write in 6502 assembly, it was rewritten in DVM code and makes following the code somewhat easier when you get accustomed to it. Finally, any class initializer is called to set up the class. Calling code asynchronously, as the class initializer is done, is covered in the frame manager code (frame.s).
Class Class (classclass.s)
--------------------------
Class references are held in a table. There can be no more than 128 classes loaded in a program. With the other memory constraints of VM02, this shouldn't be a problem. In fact, the default table size is currently 64 entries. Class data strutures can be interrogated through the routines found in this file. If the class data structure changes in the future, most of the changes can be limited to this file. Java uses strings from the class file to name classes, methods, and fields. Routines to search the class table and class structure to resolve the name and description are here. These are called from the (ops.s) bytecode interpreter whenever method invokations are made or fields are accessed. The interpreter will stick the handles resolved by the routines here into the class structure so that the string names will no longer have to be resolved, the handle will be used directly.
Threads (thread.s)
------------------
Again, pre-emptive multithreading was a design requirement even before a JVM was settled on. The Apple II is probably the least friendly to pre-emptive multi-threading of any computer. The 6502 just doesn't fit well into a threaded environment, unless that environment is a VM. Then, adding pre-emptive multi-threading to a virtual instruction set becomes pretty easy. Since the 6502 will be interpreting the Java bytecodes, I simply implemented a byte counter after each interpreted bytecode and call the scheduler when it reaches zero. The scheduler can also be called directly by way of THREAD_YIELD. If an asynchronous event requires the scheduler to run immediately (such as an interrupt waking up a thread), the count can be set to one so that the scheduler will run after the current bytecode finishes up. The scheduler itself has a table of threads which it scans looking for the best candidate to execute. This table is fixed and represents the maximum threads in the system. A define in global.inc sets the maximum number of threads (current default is 4). Each thread has 20 bytes of zero page for the bytecode interpreter state plus the 6502 hardware stack. Both are swapped during a thread re-schedule. Only the active part of the 6502 stack is read/written which is rarely more than 16 bytes. Swapping threads is pretty light-weight. If there are no threads ready to run, the idle loop will call the garbage collector to iteratively do any collecting and swapping of unaccessed memory. The GC will wait for a configurable time before starting, then it will delay between each iteration. Threads can wait on I/O events, synchronized objects, or just sleep. Threads wait on I/O by using a bitmask to identify which slot it is waiting on. Multiple slots can be waited on by setting the appropriate bits. The notifying slot will be returned to the thread in the accumulator (or multiple slots if more than one had pending I/O). If a timeout is included, VM02 will wake the thread after the timeout expires with an InterruptedException. Because the Apple II has no time base by default, it will estimate time based on the number of bytecodes interpreted. This is a pretty coarse estimate, but is a good first order approximation. The Apple IIc and any Apple II with a mouse card can use the VBL interrupt to establish a consistent time base. Timing then becomes quite accurate, at least to the resolution of 60 Hz. Other timing hardware can implement the time base with the addition of a device driver.
Execution Frames and Code Manager (frames.s codemgr.s)
------------------------------------------------------
Whenever a thread of execution invokes a new method, a frame must be created to hold the local state for that method. Conversely, when a method is exited it's frame must be broken down and cleaned up. With Java and automatic garbage collection and exception handling, this becomes somewhat of an involved task. There are two ways to invoke methods. The most common is through the invoke bytecodes that call interfaces, static, and virtual methods. The other way to call is asynchronously by way of class loading or finalizers. Current state is pushed in a way that it can be restored when it exits. Either way, the called method is checked for a native vs bytecode implementation. Native, 6502 machine code, is then called directly without further ado, making native methods very lightweight. For bytecode methods, the code manager is used to recall important details about the method so a stack frame can be allocated. Once allocated, the current frame execution state is saved and the new frame is prepared for execution. The parameters are pulled off the stack and copied into the local variables. Object parameters get their reference count incremented. Finally, all the local variable types are set to zero.
When the method exits, the local variables are scanned for object references and reference count decremented. If the method is returning due to an exception, then exception handling code is searched for and run if found. If the method was asynchronous, the previous execution state is restored and returned to. Realize that asynchronous method calls return to 6502 code (usually the class loader for <clinit>() or the object unreference routine for finalize()).
Bytecode Interpreter (ops.s)
----------------------------
The Java bytecode interpreter is mostly located in the 2nd bank of the Language Card memory. Due to the large size of the interpreter, some of the code lies in main memory. Floating point instructions are optional although the are built by default. They can be disabled by a define in global.inc, freeing up valuable main memory. In the 128K configuration, bytecodes are be read from auxiliary memory. Care must be taken to keep code that reference aux main memory in the language card. Main memory code cannot directly read from aux main memory. The unenhanced IIe doesn't like the aux memory mapped in when interrupts are enabled. The same is true when accessing other tables in aux langauage card space (memmgr.s strpool.s classclass.s). Interrupts are disabled whenever aux memory is accessed.
The interpreter optimizes access to fields and methods by caching handles and offsets (classclass.s) in the constant pool the first time they are accessed. The MSB will be set for cached values. Class, field, and method reference code will check the MSB to determine if a full lookup is required. With classes, this can lead to loading a class (and it's superclasses) the first time it is referenced. Once a class is in memory, it can never be removed.
The bytecode interpreter implements pre-emptive mutlithreading by calling the scheduler whenever opcount reaches zero. Runtime errors and exceptions are implemented by asynchronously calling a class - apple2/SystemException, that will throw the exception associated with parameter. This keeps all the exceptions themselves out of memory unless they are required. The asynchronous call is described more in the frame manager.
System Calls (sysclass.s)
-------------------------
VM02 provides a low level interface into the VM itself. Memory $0300-$03E0 is a jump table to various routines, as well as the device drivers. The apple2/vm02 class has an interface which provides a system level call into the VM or ROM, depending on the address. Addresses below $0100 will be treated as calls through the jump table, not zero page calls as this makes no sense with VM02. Global.inc contains the definitions of the jump table entries.
Garbage Collection and Finalizers (memmgr.s sysclass.s)
-------------------------------------------------------
The UNREF_OBJECT routine is located here. All object reference decrements are done here so that the recursive calls to unreference other objects can be somewhat managed. This is one of the most nerve-wracking routines you will ever encounter. Instance field references and array references are recursively dereferenced. Oh my head.
GC and finalizers are closely related in VM02. Memory is reference counted and reclaimed immediately when the count reaches zero. VM02 is extremely resource constrained, so deferring GC makes little sense. When the memory being freed is an object, it's finalizer is asynchronously called first. Once the finalizer is called, all the object referenced from this object have their reference counts decremented, and recursively freed if zero. This code gets quite messy with all the possible recursion. Care must be taken when modifying any of it.
It must be noted that reference counted GC is hardly robust. It is quite easy to have a dangling reference keep memory around, or mutually reference objects to never be freed (even if indirectly referenced). One can help the GC recover memory by explicitly setting unused references to null. This is been known to help other GC implementations as well, so isn't as horrible and anti-GC as it sounds.
Exceptions (except.s)
---------------------
Exceptions are handled through a combination of the bytecode interpreter and the frame manager. When built with DEBUG enabled, VM02 will spit out a stack trace with a little extra information. VM02 doesn't incorporate all the exception logging and trace information that a real JVM does.
Device Drivers and I/O (io.s *drvr.s)
-----------------------------
Device drivers were one of the last things added to VM02. In the middle of functional development, it was clear that adding support for additional hardware was needed but ProDOS didn't provide a driver model and VM02 didn't want to be loaded down with static hardware support. So, basic device driver laoding was added. Interrupts are fully supported through a pseudo-priority scheme that scans lower numbered slots first. Read/write and control entry points in the VM02 jump table call into the device drivers for each slot. Every slot has a real or dummy control routine that will identify the device (or no device) in that slot. One requirement of the device driver is address independence. The driver can be laoded into any avaiable memory, so it must be written position independent. With the very simple functions required of the device driver, this isn't too much of a problem. Some fixups can also be done at driver load time (mousedrvr.s) Look to the included drivers for examples. Interrupts from devices can wake up threads waiting on I/O for that slot.
Device I/O is abstracted using a slot index, not unlike file descriptors in unix. However, the Apple only has seven slots so things are a little simpler. In io.s, all the devices VM02 supports with a device driver are scanned and the driver is attached to whichever slot the hardware is found. Entries in the LINK page are fixed up to point to the driver entrypoints for that slot (global.inc). The only slot that is hard-coded is slot #3, defined as the console. Java code can interrogate each slot in turn, looking for a unique ID (defined in global.h) for the type of hardware it's looking for. Once the slot(s) is identified, it can proceed to make IOCTL calls and READ/WRITE calls. The calls are handled in a device dependent way - the mouse driver will respond to READ differently than a serial port driver. A thread will block waiting for input if nothing is available. Output usually waits for available space to write before returning. A thread can wait on multiple devices at the same time. See TestSelect.java to see how a thread can wait on keyboard and mouse input simultaneously. A bitmask is returned signaling which slot(s) have available input. A thread can also set a timeout before waiting on input. If the timeout occurs before input arrives, an InterruptedException will be generated. Again, TestSelect.java is a good example as well as org/vm02/cui/cui.java.
One nice thing that the architecture of VM02 allowed was to treat the Uthernet card just like an interrupting device. Devices are polled during THREAD_YIELD if no timer hardware is installed, originally to have a type-ahead buffer for the keyboard. I tweaked it slightly when I added the Uthernet driver to generalize it for all devices. If timer hardware (like the mouse) exists, everything gets polled 60 times a second - regardless wether they generate IRQs or not. The Uthernet driver checks for any new packets when finished servicing the current packet to keep bandwidth high.
Dave's Virtual Machine (dvm.s)
------------------------------
In order to overcome some deficencies of the 6502 processor, namely 16 bit operations and position independence, a small pseudo, or virtual, machine was written. Somewhat of a cross between the Pascal p-machine and Sweet-16, it offers a compact code representation with full 16 bit operation and position independence. This allows non-time critical code to be implemented in a manner to take up less space, be moveable (and swappable), or both. The file class loader and fixed-address memory allocater was re-implemented using DVM to reduce space, simplify code, and allow it to be swapped in and out when needed. See below for a thorough description.
I originally pulled VM02 back out of mothballs to add Uthernet and TCP/IP support. I quickly ran out of memory, so that is why I had to implement swapping. What a round-about way to get networking! It quickly became apparent that a Java solution to TCP/IP would be just too big. My ARP test pretty much consumes a 64K machine. So DVM to the rescue. VM02 has a requirement of position independent code for native methods - doing an entire TCP/IP stack in 6502 like that would be killer. I created DVM to fill just such a problem The screen driver for the CUI classes (org/vm02/cui/cuiDriver.clasm) is written in a DVM+6502 hybrid, so I'm pretty confident in it's ability. 6502 code can be easily inserted for inner loop performance critical stuff.
Object Reference Definition
===========================
Object references are 32 bits, just like the other basic data types, int and float. The value is broken up into three parts. The lower 16 bits are the memory handle to the object memory. The next higher byte is the class index, and the highest byte is class specific type data. For strings, it's the hash value of the string. For arrays, it's the type and dimension of the array. If a class doesn't have a specific use for the fourth byte, just replicate the class index byte - this makes it easy to identify object handles when debugging.
Psuedo Machine for VM coding
============================
Because the JVM is such a large app, a more sophisticated and compact code representation was needed to make it fit in the limited space of the Apple II. Macros were created to assemble into a p-code that is interpreted by yet another VM. Non-performance critical code is interpreted to save space. Native methods have access to DVM through a LINK_TABLE entrypoint. This is quite beneficial as native methods require position independence. Look at org/vm02/cui/cuiDriver.clasm for a sophisticated DVM method implementation. It is also quite easy to intersperse 6502 code inside DVM blocks. Use DVM for the position independence and overall control, 6502 for tight inner loops.
The p-code is defined as such:
$00-$07/$80-$87
LD0B (Load 0 Byte) - load zero value byte on stack
LD0W (Load 0 Word) - load zero value word on stack
LD1B (Load 1 Byte) - load one value byte on stack
LD1W (Load 1 Word) - load one value word on stack
LD2B (Load 2 Byte) - load two value byte on stack
LD2W (Load 2 Word) - load two value word on stack
LD3B (Load 3 Byte) - load three value byte on stack
LD3W (Load 3 Word) - load three value word on stack
LD4B (Load 4 Byte) - load four value byte on stack
LD4W (Load 4 Word) - load four value word on stack
LD5B (Load 5 Byte) - load five value byte on stack
LD5W (Load 5 Word) - load five value word on stack
DUPB (DUPlicate Byte) - copy top byte on stack
DUPW (DUPlicate Word) - copy top word on stack
DUP2B (DUPlicate 2 Bytes) - copy top two bytes on stack (same as DUPW)
DUP2W (DUPlicate 2 Words) - copy top two words on stack
$08-$0F/$88-$8F
SWAPB (SWAP Bytes) - swap bytes on stack
SWAPW (SWAP Words) - swap words on stack
LDCB (LoaD Constant Byte) - load constant value byte onto stack
LDCW (LoaD Constant Word) - load constant value word onto stack
LDZPB (LoaD Zero Page Byte) - load zero page memory onto stack
LDZPW (LoaD Zero Page Word) - load zero page memory onto stack
LDB (LoaD Byte) - load absolute memory onto stack
LDW (LoaD Word) - load absolute memory onto stack
LDPB (LoaD Pointer Byte) - load memory thru pointer+offset onto
stack
LDPW (LoaD Pointer Word) - load memory thru pointer+offset onto
stack
LDPINCB (LoaD Pointer Inc Byte) - load memory thru pointer onto stack
and increment pointer
LDPINCW (LoaD Pointer Inc Word) - load memory thru pointer onto stack
and increment pointer
LDPDECB (LoaD Pointer Dec Byte) - load memory thru pointer onto stack
and decrement pointer
LDPDECW (LoaD Pointer Dec Word) - load memory thru pointer onto stack
and decrement pointer
LDINDB (LoaD INDirect Byte) - load memory thru pointer from stack
onto stack
LDINDW (LoaD INDirect Word) - load memory thru pointer from stack
onto stack
$10-$17/$90-$97
POPB (POP Byte) - remove top byte on stack
POPW (POP Word) - remove top word on stack
POP2B (POP 2 Bytes) - remove top two bytes on stack (POPW)
POP2W (POP 2 Words) - remove top two words on stack
STZPB (STore Zero Page Byte) - store zero page memory from stack
STZPW (STore Zero Page Word) - store zero page memory from stack
STB (STore Byte) - store absolute memory from stack
STW (STore Word) - store absolute memory from stack
STPB (STore Pointer Byte) - store memory thru pointer+offset from
stack
STPW (STore Pointer Word) - store memory thru pointer+offset from
stack
STPINCB (STore Pointer INC Byte) - store memory thru pointer from stack
and increment pointer
STPINCW (STore Pointer INC Word) - store memory thru pointer from stack
and increment pointer
STPDECB (STore Pointer DEC Byte) - store memory thru pointer from stack
and decrement pointer
STPDECW (STore Pointer DEC Word) - store memory thru pointer from stack
and decrement pointer
STINDB (STore INDirect Byte) - store memory thru pointer, both from
stack
STINDW (STore INDirect Word) - store memory thru pointer, both from
stack
$18-$1F/$98-$9F
ZEXTB (Zero EXTend Byte) - zero extend byte to word
SEXTB (Sign EXTend Byte) - sign extend byte to word
NEGB (NEGate Byte) - negate top byte on stack
NEGW (NEGate Word) - negate top word on stack
NOTB (NOT Byte) - not top byte on stack
NOTW (NOT Word) - not top word on stack
ADDB (ADD Byte) - add top bytes on stack
ADDW (ADD Word) - add top words on stack
SUBB (SUBtract Byte) - add top bytes on stack
SUBW (SUBtract Word) - add top words on stack
ANDB (AND Bytes) - and top bytes on stack
ANDW (AND Words) - and top words on stack
ORB (OR Bytes) - or top bytes on stack
ORW (OR Words) - or top words on stack
XORB (XOR Bytes) - xor top bytes on stack
XORW (XOR Words) - xor top words on stack
$20-$27/$A0-$A7
BRZB (BRanch Zero Byte) - branch byte on stack zero
BRZW (BRanch Zero Word) - branch word on stack zero
BRNZB (BRanch Not Zero Byte) - branch byte on stack not zero
BRNZW (BRanch Not Zero Word) - branch word on stack not zero
BRPOSB (BRanch Positive Byte) - branch byte on stack positive
BRPOSW (BRanch Positive Word) - branch word on stack positive
BRNEGB (BRanch NEGative Byte) - branch byte on stack negative
BRNEGW (BRanch NEGative Word) - branch word on stack negative
BREQUB (BRanch EQUal Bytes) - branch if top bytes equal
BREQUW (BRanch EQUal Words) - branch if top words equal
BRNEQB (BRanch Not EQual Bytes) - branch if top bytes not equal
BRNEQW (BRanch Not EQual Words) - branch if top words not equal
BRGTB (BRanch Greater Than Bytes) - branch if top-1 byte greater than top
BRGTW (BRanch Greater Than Words) - branch if top-1 word greater than top
BRLEB (BRanch Less than or Equal Bytes) - branch if top-1 byte less or equal than top
BRLEW (BRanch Less than or Equal Words) - branch if top-1 word less or equal than top
$28-$2F/$A8-$AF
BRAB (BRanch Above Bytes) - branch if top-1 byte bigger than top
BRAW (BRanch Above Words) - branch if top-1 word bigger than top
BRBEB (BRanch Below or Equal Bytes) - branch if top-1 byte bigger than top
BRBEW (BRanch Below or Equal Words) - branch if top-1 word bigger than top
BRNCH (BRaNCH) - branch to offset
""
DECJNZB (DECrement memory Jump Not Zero Byte) - decrement memory byte and
jump not zero
DECJNZW (DECrement memory Jump Not Zero Word) - decrement memory word and
jump not zero
SHLB (SHift Left Byte) - shift byte on stack left
SHLW (SHift Left Word) - shift word on stack left
SHRB (SHift Right Byte) - shift byte on stack right
SHRW (SHift Right Word) - shift word on stack right
INCRB (INCRement Byte) - increment byte on stack
INCRW (INCRement Word) - increment word on stack
DECRB (DECRement Byte) - decrement byte on stack
DECRW (DECRement Word) - decrement word on stack
$30-$37/$B0-$B7
EXIT (EXIT) - exit VM
""
JUMP (JUMP) - jump to address
JUMPIND (JUMP) - jump to address on stack
CALL (CALL) - call subroutine address
CALLIND (CALL) - call subroutine address on stack
RET (RETurn) - return from subroutine
""
CALL_02 (CALL 6502) - call 6502 subroutine address
CALLIND_02 (CALL 6502) - call 6502 subroutine address on stack
SWTCHB (SWiTCH Byte) - switch to matching byte value on stack
SWTCHW (SWiTCH Word) - switch to matching word value on stack
This is what an example DVM program looks like that fills the hi-res screen in an incrementing value until a key is pressed:
.INCLUDE "dvm.inc"
HRPTR = $60
.CODE
LDX #$FF
TXS
DVM_BEGIN
CALL GRAPHMODE ; BASIC CALL TO SUBROUTINE
LD0B ; SET INITIAL VALUE TO ZERO
STB HRVAL
HRFILL: LDCW $4000 ; HR ADDRESS
STZPW HRPTR ; STORE IN ZP
LDCW $1000 ; HR SIZE IN WORDS
STW HRCOUNT
LDB HRVAL
DUPB ; EXPAND FILL TO WORD SIZE
HRLOOP: DUPW
STPIW (HRPTR) ; FILL SCREEN A WORD AT A TIME
DECJNZW HRCOUNT, HRLOOP ; HANDY LOOP OPCODE
POPB
INCRB
STB HRVAL
LDB $C000 ; CHECK KEYBOARD
BRPOSB HRFILL
LDB $C010 ; CLEAR STROBE
POPB ; THROW AWAY VALUE
LDCW TEXTMODE ; INDIRECT CALL TO SUBROUTINE
CALLIND ; CALL ADDRESS IS ABSOLUTE
JUMP HREXIT ; SIGNED 16 BIT OFFSET DEST
GRAPHMODE: LDB $C057
POPB
LDB $C050
POPB
LDB $C055
POPB
RET
TEXTMODE: DVM_END ; POP OUT OF DVM FOR THINGS
LDA $C051 ; BETTER DONE IN 6502
LDA $C054
DVM_BEGIN
RET
HREXIT: DVM_END
RTS
HRVAL: .BYTE 0
HRCOUNT: .WORD 0

280
LICENSE.TXT Executable file
View File

@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

88
README.TXT Executable file
View File

@ -0,0 +1,88 @@
VM02 Apple II Java Virtual Machine
==================================
Introduction:
-------------
VM02 is a Java compatible environment for the Apple II series of computers. Most basic features of a Java VM are implemented to allow direct execution of standard Java class files.
However, in order to fit in the constraints of a 64K, 8 bit, 1 MHz computer, some aspects of a JVM have been reduced or removed.
What is implemented:
- Standard Java class file loading and execution.
- Full object inheritence
- Multithreading (4 threads max by default)
- Exception handling
- Arrays up to 15 dimension
- 32 bit integers and single precision floating point
- Garbage collection
- Demand memory swapping to RAM disk or other available volume
- Finalizers
- Software and hardware implemented timing
- Device drivers for common hardware
- 128K memory support for //e and //c
- Exec-ing another class
What is missing:
- 64 bit data types unimplemented
- Standard class library missing or limited implementation
- Lots of memory
Running VM02:
-------------
VM02 is run by executing the JAVA.SYSTEM file. You must start JAVA.SYSTEM from the prefix where it exists. This is required to find the class base path to the support classes. Parameter passing is supported if your shell implements it. If no paramter is passed in and a file named STARTUP exists, it will be loaded as the initial class. Otherwise, you will be greeted with with a prompt to type in the class name to execute. You can start classes in other directories by typing in the fully qualified path. Class filenames under ProDOS are missing the ".class" suffix due to limited filename lengths. For example, to execute the Hello.class file on the /VM02.EXTRAS/SAMPLES volume, with VM02 you would type:
/VM02.EXTRAS/SAMPLES/HELLO
at the "Main class:" prompt. You would see this:
Main class:/VM02.EXTRAS/SAMPLES/HELLO
and then VM02 would attempt to find and load the class file. If an error occurs along the way, you will get an Unhandled Exception message, probably because the file (or superclass file) was not found. The preferred directory structure is to place all classes as subdirectories of the main VM02 directory.
Model Compatibility:
--------------------
VM02 has been tested on a variety of Apple II models from a Rev 0 Apple ][ with Integer Basic to a //c+ and much in-between. Most models run without issue, however some need a little support. Noteably, my ROM 1 IIgs needs to run VM02 under GS/OS to run without problems. Without pathces applied by GS/OS, the system quickly gets random values blasted throughout memory. When I added support for 128K and more sophisticated memory management, it caused the IIgs to falter more and more. Rock solid under GS/OS. I was unable to test a ROM 3 IIgs, so I don't know if those problems persist. Earlier models of the //c might lose VBL interrupts after being run for awhile. The unenhanced IIe should run the CUI programs but will look funny without the MouseText characters. Pre-IIe models won't run the CUI programs at all.
Implementation of VM02:
-----------------------
VM02 is implemented from scratch in 6502 assembly, optimized for the 6502 and Apple II platform. The entire VM fits in about 20K of RAM. This leaves about 30K of RAM available for data and code in the 64K version, or about 32K for data and 44K for bytecode in the 128K version. Not a lot, but Java was defined for small platforms so you can get some reasonable programs even in such a small space. Swapping from main memory extends the available RAM, dependent on the swap volume. A new low level pseudo-machine was written to implement some of the non-time critical routines. Somewhat of a cross between the Pascal p-machine and Sweet-16, DVM (Dave's Virtual Machine) is optimized for position independent 16 bit operations. The class loader and some memory manager routines are implemented in DVM to save code space (about 4X) or allow for on-the-fly relocation. A high-resolution graphics page is available for use with small programs. Performance is acceptable for a 1 MHz, 8 bit CPU; on par with the UCSD p-System VM used in Apple Pascal. All features of the Apple II are made available through either specific classes, a low-level class that can call ROM routines, or both. There is a device driver architecture that currently supports 80 column cards, the Super Serial card, and the Apple Mouse card with preliminary support of the Uthernet card and Apple Graphics Tablet. Probing for the Uthernet card can cause problems in some machines with hardware VM02 doesn't detect. If probing for the Uthernet card causes the machine to hang at VM02 init time, hold down the Open-Apple key (or PushButton 1) during initialization and the Uthernet probe will be skipped.
As noted earlier, class filenames are missing the ".class" suffix, however the file type of $ED is used to identify class files and are given the type identifier of JVM. System classes are in the JAVA/* subdirectories. Apple II specific classes are found in the APPLE2/ directory. VM02 specific class are found in the ORG/VM02/* subdirectories.
Developing for VM02:
--------------------
There isn't a native Java compiler for the Apple II (yet). All Java source must be cross-developed on a capable computer. The resulting class file needs to be transferred to an Apple or emulator using any number of available tools. One way is to use a tool like a2tools, AppleCommander, or CiderPress to copy the class file to a .DSK disk image file. The disk image can be run directly from an emulator or copied to real hardware with ADT or ADTPro. To compile against the Apple II specific classes, either download the source package (you will also need cc65, the 6502 C compiler package) or copy the APPLE2/ directory contents and rename the files with mixed case and add the .class suffix.
The sample classes:
-------------------
Included with VM02 are sample classes that show off the capabilites of the environment. They can be executed from the samples disk or copied to another location.
HELLO - A simple text entry program
HELLOWORLD - sample multi-threaded program
MOIRE - Hires example, requires AppleSoft in ROM
HIRESDEMO - Another hires demo
RODSCOLORS - Lores demo
SIMPLEPONG - Lores demo with paddle input
TERMINAL - Simple terminal program using the SSC
VOLUMES - List online volumes
CATALOG - Catalog a directory
LIST - List a text file to the screen
STRESSMEM - Allocate 64K 2D array. Stresses swapping code
TESTSELECT - select() type call using mouse, keyboard, and timeouts
TESTCHAIN - Chain to another class passing parameters and exit status
TESTCUI - Show off the CUI's controls (needs 128K)
The Launcher class:
-------------------
With the release of version 1.0, a file launcher is included to navigate through volumes and directories and execute Java classes. Files of type $ED show up as JVM executable files.
Release Version 1.0:
--------------------
This represents the first release of VM02. Much of the implementation exists as skeletons of a complete Java environment. There is enough to run basic programs and do minimalistic file I/O.
Known issues:
-------------
No know issues exist.
March 31st 2010, 11:59:59 PM
Dave Schmenk...

126
plasma2/FIRE.PLA#040000 Executable file
View File

@ -0,0 +1,126 @@
CONST FALSE = 0
CONST TRUE = NOT FALSE
CONST SHOWLORES = $C056
CONST KEYBOARD = $C000
CONST KEYSTROBE = $C010
CONST EMPTY = 0
CONST TREE = 4
CONST FIRE = 13
CONST FORESTSIZE = 42*42
BYTE HELLOMSG[] = "PRESS ANY KEY TO BEGIN..."
BYTE EXITMSG[] = "PRESS ANY KEY TO EXIT."
BYTE GOODBYE[] = "THAT'S ALL FOLKS!"
BYTE TREES1[FORESTSIZE]
BYTE TREES2[FORESTSIZE]
WORD RNDNUM
DEF TEXTMODE
DROP ROMCALL(0, 0, 0, 0, $FB39)
END
DEF HOME
DROP ROMCALL(0, 0, 0, 0, $FC58)
END
DEF GOTOXY(X, Y)
^($24) = X
DROP ROMCALL(Y, 0, 0, 0, $FB5B)
END
DEF GRMODE
DROP ROMCALL(0, 0, 0, 0, $FB40)
DROP ^SHOWLORES
END
DEF RANDOMIZE(SEED)
RNDNUM = (SEED >> 8) + (SEED << 8) + SEED
END
DEF RND
RNDNUM = (RNDNUM << 8) + RNDNUM + 12345
RETURN RNDNUM & $7FFF
END
DEF BYFIRE(TREEPTR)
IF ^(TREEPTR - 43) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR - 42) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR - 41) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR - 1) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR + 1) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR + 41) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR + 42) == FIRE
RETURN TRUE
ELSIF ^(TREEPTR + 43) == FIRE
RETURN TRUE
FIN
RETURN FALSE
END
DEF FORESTFIRE
WORD NEWTREES, OLDTREES, NEWTREE, OLDTREE, YROW
BYTE X, Y
MEMSET(EMPTY, @TREES1, FORESTSIZE)
MEMSET(EMPTY, @TREES2, FORESTSIZE)
OLDTREES = @TREES1
NEWTREES = @TREES2
FOR Y = 1 TO 40
YROW = Y * 42
FOR X = 1 TO 40
IF RND < 8000
^(OLDTREES + X + YROW) = TREE
FIN
NEXT
NEXT
WHILE ^$C000 < 128
FOR Y = 1 TO 40
YROW = Y * 42
FOR X = 1 TO 40
OLDTREE = OLDTREES + X + YROW
NEWTREE = NEWTREES + X + YROW
WHEN ^OLDTREE
IS EMPTY
IF RND < 5000
^NEWTREE = TREE
ELSE
^NEWTREE = EMPTY
FIN
IS TREE
IF RND < 5 OR BYFIRE(OLDTREE)
^NEWTREE = FIRE
ELSE
^NEWTREE = TREE
FIN
IS FIRE
^NEWTREE = EMPTY
WEND
DROP ROMCALL(^NEWTREE, 0, 0, 0, $F864)
DROP ROMCALL(Y - 1, 0, X - 1, 0, $F800)
NEXT
NEXT
NEWTREES =, OLDTREES = OLDTREES, NEWTREES
LOOP
DROP ^$C010
END
PRSTR(@HELLOMSG)
WHILE ^$C000 < 128
RNDNUM = RNDNUM + 1
LOOP
RANDOMIZE(RNDNUM)
DROP ^$C010
GRMODE
HOME
GOTOXY(10,22)
PRSTR(@EXITMSG)
FORESTFIRE
TEXTMODE
HOME
PRSTR(@GOODBYE)
DONE

1
plasma2/autorun#040000 Executable file
View File

@ -0,0 +1 @@
PLED READ.ME

BIN
plasma2/bytedump Executable file

Binary file not shown.

29
plasma2/bytedump.c Executable file
View File

@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
unsigned char buf[256];
int main(int argc, char **argv)
{
int fd, offset;
if (argc < 2)
{
printf("Usage: %s <binfile>\n", argv[0]);
return (1);
}
if ((fd = open(argv[1], O_RDONLY, 0)) > 0)
{
offset = 0;
while (read(fd, buf, 1) == 1)
{
if (offset++ & 0x0F)
printf(",$%02X", buf[0]);
else
printf("\n\t.BYTE\t$%02X", buf[0]);
}
printf("\n");
}
close(fd);
}

492
plasma2/cmd.pla Executable file
View File

@ -0,0 +1,492 @@
const iobuffer = $0800
const databuff = $0C00
const autorun = $01FF
byte version[] = "PLASMA ][ VM VERSION 0.8"
byte errorstr[] = "ERROR: $"
byte okstr[] = "OK"
byte prefix[32] = ""
byte perr
word cmdptr
;
; Utility functions
;
; CALL PRODOS
; SYSCALL(CMD, PARAMS)
;
asm prodos
LDA ESTKL,X
LDY ESTKH,X
STA PARAMS
STY PARAMS+1
INX
LDA ESTKL,X
STA CMD
STX ESP
JSR $BF00
CMD: DB 00
PARAMS: DW 0000
BIT LCBNK2
LDX ESP
STA ESTKL,X
LDY #$00
STY ESTKH,X
end
;
; CALL LOADED SYSTEM PROGRAM
;
asm exec
LDX #$FF
TXS
BIT ROMIN
JMP $2000
end
;
; SET MEMORY TO 0
; MEMCLR(ADDR, SIZE)
;
asm memclr
LDY #$00
LDA ESTKL+1,X
STA DSTL
LDA ESTKH+1,X
STA DSTH
INC ESTKL,X
INC ESTKH,X
TYA
SETMLP: DEC ESTKL,X
BNE :+
DEC ESTKH,X
BEQ :++
: STA (DST),Y
INY
BNE SETMLP
INC DSTH
BNE SETMLP
: INX
INX
end
;
; COPY MEMORY
; MEMCPY(SRCADDR, DSTADDR, SIZE)
;
asm memcpy
LDY #$00
LDA ESTKL,X
BNE :+
LDA ESTKH,X
BEQ MEMEXIT
: LDA ESTKL+1,X
STA DSTL
LDA ESTKH+1,X
STA DSTH
LDA ESTKL+2,X
STA SRCL
LDA ESTKH+2,X
STA SRCH
CMP DSTH
BCC REVCPY
BNE FORCPY
LDA SRCL
CMP DSTL
BCS FORCPY
REVCPY: ; REVERSE DIRECTION COPY
; CLC
LDA ESTKL,X
ADC DSTL
STA DSTL
LDA ESTKH,X
ADC DSTH
STA DSTH
CLC
LDA ESTKL,X
ADC SRCL
STA SRCL
LDA ESTKH,X
ADC SRCH
STA SRCH
INC ESTKH,X
REVCPYLP:
LDA DSTL
BNE :+
DEC DSTH
: DEC DSTL
LDA SRCL
BNE :+
DEC SRCH
: DEC SRCL
LDA (SRC),Y
STA (DST),Y
DEC ESTKL,X
BNE REVCPYLP
DEC ESTKH,X
BNE REVCPYLP
BEQ MEMEXIT
FORCPY: INC ESTKH,X
FORCPYLP:
LDA (SRC),Y
STA (DST),Y
INC DSTL
BNE :+
INC DSTH
: INC SRCL
BNE :+
INC SRCH
: DEC ESTKL,X
BNE FORCPYLP
DEC ESTKH,X
BNE FORCPYLP
MEMEXIT: INX
INX
INX
end
;
; CHAR OUT
; COUT(CHAR)
;
asm cout
LDA ESTKL,X
INX
ORA #$80
BIT ROMIN
JSR $FDED
BIT LCBNK2
end
;
; CHAR IN
; RDKEY()
;
asm cin
BIT ROMIN
STX ESP
JSR $FD0C
LDX ESP
BIT LCBNK2
DEX
STA ESTKL,X
LDY #$00
STY ESTKH,X
end
;
; PRINT STRING
; PRSTR(STR)
;
asm prstr
LDY #$00
LDA ESTKL,X
STA SRCL
LDA ESTKH,X
STA SRCH
BIT ROMIN
LDA (SRC),Y
STA ESTKL,X
BEQ :+
_PRS1: INY
LDA (SRC),Y
ORA #$80
JSR $FDED
TYA
CMP ESTKL,X
BNE _PRS1
: INX
BIT LCBNK2
end
;
; PRINT BYTE
;
asm prbyte
LDA ESTKL,X
INX
STX ESP
BIT ROMIN
JSR $FDDA
BIT LCBNK2
LDX ESP
end
;
; READ STRING
; STR = RDSTR(PROMPTCHAR)
;
asm rdstr
LDA ESTKL,X
STA $33
STX ESP
BIT ROMIN
JSR $FD6A
BIT LCBNK2
STX $01FF
: LDA $01FF,X
AND #$7F
STA $01FF,X
DEX
BPL :-
LDX ESP
LDA #$FF
STA ESTKL,X
LDA #$01
STA ESTKH,X
end
asm toupper
LDA ESTKL,X
CMP #'a'
BCC :+
CMP #'z'+1
BCS :+
SEC
SBC #$20
STA ESTKL,X
:
end
;
; EXIT
;
asm reboot
BIT ROMIN
LDA #$00
STA $3F4 ; INVALIDATE POWER-UP BYTE
JMP ($FFFC) ; RESET
end
def crout
cout($0D)
end
;
; ProDOS routines
;
def getpfx(path)
byte params[3]
^path = 0
params.0 = 1
params:1 = path
perr = prodos($C7, @params)
return path
end
def setpfx(path)
byte params[3]
params.0 = 1
params:1 = path
perr = prodos($C6, @params)
return path
end
def online
byte params[4]
params.0 = 2
params.1 = 0
params:2 = $2000
perr = prodos($C5, @params)
return $2000
end
def open(path, buff)
byte params[6]
params.0 = 3
params:1 = path
params:3 = buff
params.5 = 0
perr = prodos($C8, @params)
return params.5
end
def close(refnum)
byte params[2]
params.0 = 1
params.1 = refnum
perr = prodos($CC, @params)
return perr
end
def read(refnum, buff, len)
byte params[8]
params.0 = 4
params.1 = refnum
params:2 = buff
params:4 = len
params:6 = 0
perr = prodos($CA, @params)
return params:6
end
;
; Command mode
;
def volumes
word strbuf
byte i
strbuf = online()
for i = 0 to 15
^strbuf = ^strbuf & $0F
if ^strbuf
cout('/')
prstr(strbuf)
crout()
fin
strbuf = strbuf + 16
next
end
def catalog(optpath)
byte path[64]
byte refnum
byte firstblk
byte entrylen, entriesblk
byte i, type, len
word entry, filecnt
if ^optpath
memcpy(optpath, @path, ^optpath + 1)
else
drop getpfx(@path)
prstr(@path)
crout()
fin
refnum = open(@path, iobuffer);
if perr
return perr
fin
firstblk = 1
repeat
if read(refnum, databuff, 512) == 512
entry = databuff + 4
if firstblk
entrylen = databuff.$23
entriesblk = databuff.$24
filecnt = databuff:$25
entry = entry + entrylen
fin
for i = firstblk to entriesblk
type = ^entry
if type <> 0
len = type & $0F
^entry = len
prstr(entry)
if type & $F0 == $D0 ; Is it a directory?
cout('/')
len = len + 1
elsif (entry).$10 == $FF
cout('*')
len = len + 1
fin
for len = 19 - len downto 0
cout(' ')
next
filecnt = filecnt - 1
fin
entry = entry + entrylen
next
firstblk = 0
else
filecnt = 0
fin
until filecnt == 0
drop close(refnum)
crout()
return 0
end
def stripchars(strptr)
while ^strptr and ^(strptr + 1) <> ' '
memcpy(strptr + 2, strptr + 1, ^strptr)
^strptr = ^strptr - 1
loop
return ^strptr
end
def stripspaces(strptr)
while ^strptr and ^(strptr + ^strptr) <= ' '
^strptr = ^strptr - 1
loop
while ^strptr and ^(strptr + 1) <= ' '
memcpy(strptr + 2, strptr + 1, ^strptr)
^strptr = ^strptr - 1
loop
end
def striptrail(strptr)
byte i
for i = 1 to ^strptr
if (strptr)[i] == ' '
^strptr = i - 1
return
fin
next
end
def parsecmd(strptr)
byte cmd
cmd = 0
stripspaces(strptr)
if ^strptr
cmd = ^(strptr + 1)
memcpy(strptr + 2, strptr + 1, ^strptr)
^strptr = ^strptr - 1
fin
stripspaces(strptr)
return cmd
end
def resetmemfiles
;
; Close all files
;
^$BFD8 = 0
drop close(0)
;
; Set memory bitmap
;
memclr($BF58, 24)
^$BF58 = $CF
^$BF6F = $01
end
def execsys(sysfile)
byte refnum
word len
if ^sysfile
memcpy(sysfile, $280, ^sysfile + 1)
striptrail(sysfile)
refnum = open(sysfile, iobuffer)
if refnum
len = read(refnum, $2000, $FFFF)
resetmemfiles()
if len
memcpy($280, sysfile, ^$280 + 1)
if stripchars(sysfile) and ^$2000 == $4C and *$2003 == $EEEE
stripspaces(sysfile)
if ^$2006 <= ^sysfile
memcpy(sysfile, $2006, ^sysfile + 1)
fin
fin
striptrail($280)
exec()
fin
fin
fin
end
resetmemfiles()
execsys(autorun)
prstr(@version)
crout();
while 1
prstr(getpfx(@prefix))
cmdptr = rdstr($BA)
when toupper(parsecmd(cmdptr))
is 'Q'
reboot()
is 'C'
drop catalog(cmdptr)
is 'P'
drop setpfx(cmdptr)
is 'V'
volumes();
is '-'
execsys(cmdptr)
perr = $46
wend
if perr
prstr(@errorstr)
prbyte(perr)
else
prstr(@okstr)
fin
crout()
loop
done

1372
plasma2/cmd.s Executable file

File diff suppressed because it is too large Load Diff

10
plasma2/cmdloader.cfg Executable file
View File

@ -0,0 +1,10 @@
MEMORY {
RAM: start = $1007, size = $1000, file = %O;
}
SEGMENTS {
CODE: load = RAM, type = rw;
DATA: load = RAM, type = rw;
BSS: load = RAM, type = rw;
}

1098
plasma2/codegen.c Executable file

File diff suppressed because it is too large Load Diff

55
plasma2/codegen.h Executable file
View File

@ -0,0 +1,55 @@
#define EDASM 1
void emit_flags(int flags);
int optimization(int level);
void emit_header(void);
void emit_trailer(void);
void emit_comment(char *s);
void emit_asm(char *s);
void emit_idlocal(char *name, int value);
void emit_idglobal(int value, int size, char *name);
void emit_idfunc(int value, char *name);
void emit_idconst(char *name, int value);
int emit_data(int vartype, int consttype, long constval, int constsize);
void emit_codetag(int tag);
void emit_const(int cval);
void emit_lb(void);
void emit_lw(void);
void emit_llb(int index);
void emit_llw(int index);
void emit_lab(int tag);
void emit_law(int tag);
void emit_sb(void);
void emit_sw(void);
void emit_slb(int index);
void emit_slw(int index);
void emit_dlb(int index);
void emit_dlw(int index);
void emit_sab(int tag);
void emit_saw(int tag);
void emit_dab(int tag);
void emit_daw(int tag);
void emit_call(int tag);
void emit_ical(void);
void emit_localaddr(int index);
void emit_globaladdr(int tag, int type);
void emit_globaladdrofst(int tag, int offset, int type);
void emit_indexbyte(void);
void emit_indexword(void);
int emit_unaryop(int op);
int emit_op(t_token op);
void emit_skptru(int tag);
void emit_skpfls(int tag);
void emit_skpgt(int tag);
void emit_skplt(int tag);
void emit_skpne(int tag);
void emit_skip(int tag);
void emit_swap(void);
void emit_dup(void);
void emit_push(void);
void emit_pull(void);
void emit_drop(void);
void emit_leave(int framesize);
void emit_ret(void);
void emit_def(int defopt);
void emit_enter(int framesize, int cparams);
void emit_start(void);

BIN
plasma2/codegen.o Executable file

Binary file not shown.

10
plasma2/default.cfg Executable file
View File

@ -0,0 +1,10 @@
MEMORY {
RAM: start = $2000, size = $2800, file = %O;
}
SEGMENTS {
CODE: load = RAM, type = rw;
DATA: load = RAM, type = rw;
BSS: load = RAM, type = rw;
}

189
plasma2/dumprel.pla Executable file
View File

@ -0,0 +1,189 @@
const keyboard=$C000
const keystrobe=$C010
const iobuffer=$0800
const databuff=$0C00
const inbuff=$01FF
byte loadadr[] = "LOAD ADDRESS: $"
byte datasz[] = "DATA SIZE: $"
byte RLD[] = "RELOCATION DIRECTORY:"
byte ESD[] = "SYMBOL TABLE:"
byte errstr[] = "ERROR: "
byte perr
def home
drop romcall(0, 0, 0, 0, $FC58)
end
def gotoxy(x, y)
^($24) = x
drop romcall(y, 0, 0, 0, $FB5B)
end
def prbyte(val)
drop romcall(val, 0, 0, 0, $FDDA)
end
def prword(val)
drop romcall(val >> 8, val, 0, 0, $F941)
end
def crout
drop romcall(0, 0, 0, 0, $FD8E)
end
def getpfx(path)
byte params[3]
^(path) = 0
params.0 = 1
params:1 = path
perr = syscall($C7, @params)
return path
end
def getfileinfo(path, infoptr)
byte params[18]
params.0 = 10
params:1 = path
perr = syscall($C4, @params)
if not perr
memcpy(@params.3, infoptr, 15)
fin
return perr
end
def open(path, buff)
byte params[6]
params.0 = 3
params:1 = path
params:3 = buff
params.5 = 0
perr = syscall($C8, @params)
return params.5
end
def close(refnum)
byte params[2]
params.0 = 1
params.1 = refnum
perr = syscall($CC, @params)
return perr
end
def read(refnum, buff, len)
byte params[8]
params.0 = 4
params.1 = refnum
params:2 = buff
params:4 = len
params:6 = 0
perr = syscall($CA, @params)
return params:6
end
def dumpln(memptr, ofst, len)
byte i
cout('$')
prword(ofst)
cout(':')
cout(' ')
len = len - 1
for i = 0 to len
prbyte((memptr).[i])
if i & 1
cout(' ')
fin
next
cout(' ')
for i = 0 to len
if (memptr).[i] & $7F < ' '
cout ('.')
else
cout((memptr).[i])
fin
next
crout
end
def dumprld(rld)
cout('$')
prbyte(^rld)
cout(':')
cout(' ')
cout('$')
prword(*(rld + 1))
cout(' ')
cout('$')
prbyte(^(rld + 3))
crout
return rld + 4
end
def dumpesd(esd)
while ^esd & $80
cout(^esd)
esd = esd + 1
loop
cout(^esd)
cout(':')
cout(' ')
cout('$')
prbyte(^(esd + 1))
cout(' ')
cout('$')
prword(^(esd + 2))
crout
return esd + 4
end
def dump(path)
byte refnum, info[15]
word len, ofst, datalen, rld, esd
getfileinfo(path, @info)
prstr(@loadadr)
prword(info:2)
crout
refnum = open(path, iobuffer)
len = read(refnum, databuff, 512)
datalen = databuff:0
prstr(@datasz)
prword(datalen)
crout
rld = databuff + datalen + 2
ofst = 0
while datalen > 8
dumpln(databuff + 2 + ofst, ofst, 8)
ofst = ofst + 8
datalen = datalen - 8
loop
dumpln(databuff + 2 + ofst, ofst, datalen)
crout
prstr(@RLD)
crout
while ^rld
rld = dumprld(rld)
loop
crout
prstr(@ESD)
crout
esd = rld + 1
while ^esd
esd = dumpesd(esd)
loop
drop close(refnum)
end
def getlin
^inbuff = romcall(0, 0, 0, 0, $FD6A).1
return inbuff
end
home
dump(getlin)
done

BIN
plasma2/fire#ff0000 Executable file

Binary file not shown.

269
plasma2/fire.pla Executable file
View File

@ -0,0 +1,269 @@
CONST FALSE = 0
CONST TRUE = NOT FALSE
CONST SHOWLORES = $C056
CONST KEYBOARD = $C000
CONST KEYSTROBE = $C010
CONST EMPTY = 0
CONST TREE = 4
CONST FIRE = 13
CONST FORESTSIZE = 42*42
BYTE HELLOMSG[] = "PRESS ANY KEY TO BEGIN..."
BYTE EXITMSG[] = "PRESS ANY KEY TO EXIT."
BYTE GOODBYE[] = "THAT'S ALL FOLKS!"
BYTE TREES1[FORESTSIZE]
BYTE TREES2[FORESTSIZE]
WORD RNDNUM
;
; Defines for ASM routines
;
ASM EQUATES
SRC EQU $F0
SRCL EQU SRC
SRCH EQU SRC+1
DST EQU SRC+2
DSTL EQU DST
DSTH EQU DST+1
ESP EQU DST+2
END
;
; CALL 6502 ROUTINE
; ROMCALL(AREG, XREG, YREG, STATUS, ADDR)
;
ASM ROMCALL
TMP EQU $06
PHP
LDA ESTKL,X
STA TMP
LDA ESTKH,X
STA TMP+1
INX
LDA ESTKL,X
PHA
INX
LDA ESTKL,X
TAY
INX
LDA ESTKL+1,X
PHA
LDA ESTKL,X
INX
STX TMP+2
TAX
PLA
BIT ROMIN
PLP
JSR JMPTMP
PHP
BIT LCBNK2
STA REGVALS+0
STX REGVALS+1
STY REGVALS+2
PLA
STA REGVALS+3
LDX TMP+2
LDA #<REGVALS
LDY #>REGVALS
STA ESTKL,X
STY ESTKH,X
PLP
RTS
JMPTMP: JMP (TMP)