This commit is contained in:
Martin Haye 2014-08-21 07:58:33 -07:00
commit 13eb9afbe7
8 changed files with 524 additions and 456 deletions

View File

@ -49,10 +49,10 @@ for the **make** program to build all the dependencies and run the module.
All identifiers and reserved words are case insensitive. Case is only significant inside character constants and strings. Imported and exported symbols are always promoted to upper case when resolved. Because some Apple IIs only work easily with uppercase, the eases the chance of mismatched symbol names.
### Comments
Comments are allowed throughout a PLASMA source file. The format follows that of an assembler: they begin with a `;` and comment out the rest of the line:
Comments are allowed throughout a PLASMA source file. The format follows that of C like languages: they begin with a `//` and comment out the rest of the line:
```
; This is a comment, the rest of this line is ignored
// This is a comment, the rest of this line is ignored
```
### Declarations
@ -188,7 +188,7 @@ byte smallarray[4]
byte initbarray[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
byte string[64] = "Initialized string"
word wlabel[]
word = 1000, 2000, 3000, 4000 ; Anonymous array
word = 1000, 2000, 3000, 4000 // Anonymous array
word funclist = @myfunc, $0000
```
Arrays can be uninitialized and reserve a size, as in `smallarray` above. Initilized arrays without a size specifier in the definition will take up as much data as is present, as in `initbarray` above. Strings are special arrays that include a hidden length byte in the beginning (Pascal strings). When specified with a size, a minimum size is reserved for the string value. Labels can be defined as arrays without size or initializers; this can be useful when overlapping labels with other arrays or defining the actual array data as anonymous arrays in following lines as in `wlabel` and following lines. Addresses of other data (must be defined previously) or function definitions (pre-defined with predef), including imported references, can be initializers.
@ -210,9 +210,9 @@ The override operator becomes more useful when multi-dimenstional arrays are use
##### Multi-Dimensional Arrays
Multi-dimensional arrays are implemented as arrays of arrays, not as a single block of memory. This allows constructs such as:
```
;
; Hi-Res scanline addresses
;
//
// Hi-Res scanline addresses
//
word hgrscan[] = $2000,$2400,$2800,$2C00,$3000,$3400,$3800,$3C00
word = $2080,$2480,$2880,$2C80,$3080,$3480,$3880,$3C80
```
@ -245,13 +245,13 @@ Notice how xscan goes to 39 instead of 19 in the byte accessed version.
#### Offsets (Structure Elements)
Structures are another fundamental construct when accessing in-common data. Using fixed element offsets from a given address means you only have to pass one address around to access the entire record. Offsets are specified with a constant expression following the type override specifier.
```
predef puti ; print an integer
predef puti // print an integer
byte myrec[]
word = 2
byte = "PLASMA"
puti(myrec:0) ; ID = 2
puti(myrec.2) ; Name length = 6 (Pascal string puts length byte first)
puti(myrec:0) // ID = 2
puti(myrec.2) // Name length = 6 (Pascal string puts length byte first)
```
This contrived example shows how one can access offsets from a variable as either `byte`s or `word`s regardless of how they were defined. This operator becomes more powerful when combined with pointers, defined next.
@ -275,9 +275,9 @@ const elem_id = 0
const elem_addr = 1
def addentry(entry, id, addr)
entry->elem_id = id ; set ID byte
entry=>elem_addr = addr ; set address
return entry + 3 ; return next enry address
entry->elem_id = id // set ID byte
entry=>elem_addr = addr // set address
return entry + 3 // return next enry address
end
```
The above is equivalent to:
@ -286,16 +286,16 @@ const elem_id = 0
const elem_addr = 1
def addentry(entry, id, addr)
(entry).elem_id = id ; set ID byte
(entry):elem_addr = addr ; set address
return entry + 3 ; return next enry address
(entry).elem_id = id // set ID byte
(entry):elem_addr = addr // set address
return entry + 3 // return next enry address
end
```
##### Addresses of Data/Code
Along with dereferencing a pointer, there is the question of getting the address of a variable. The `@` operator prepended to a variable name or a function definition name, will return the address of the variable/definition. From the previous example, the call to `strlen` would look like:
```
puti(strlen(@mystring)) ; would print 17 in this example
puti(strlen(@mystring)) // would print 17 in this example
```
##### Function Pointers
@ -311,16 +311,16 @@ def subvals(a, b)
end
funcptr = @addvals
puti(funcptr(5, 2)) ; Outputs 7
puti(funcptr(5, 2)) // Outputs 7
funcptr = @subvals
puti(funcptr(5, 2)) ; Outputs 3
puti(funcptr(5, 2)) // Outputs 3
```
These concepts can be combined with the structure offsets to create a function table that can be easily changed on the fly. Virtual functions in object oriented languages are implemented this way.
```
predef myinit, mynew, mydelete
export word myobject_class = @myinit, @mynew, @mydelete
; Rest of class data/code follows...
// Rest of class data/code follows...
```
And an external module can call into this library (class) like:
```
@ -390,7 +390,7 @@ Address operators can work on any value, i.e. anything can be an address. Parent
| AND | logical AND
### Statements
PLASMA definitions are a list of statements the carry out the algorithm. Statements are generally assignment or control flow in nature.
PLASMA definitions are a list of statements the carry out the algorithm. Statements are generally assignment or control flow in nature. Multiple statements one a single line are seperated by a ';'. Note, this use the comment seperator in previous versions of PLASMA. It is generally considered bad form to have more than one statement per line unless they are very short.
#### Assignment
Assignments evaluate an expression and save the result into memory. They can be very simple or quite complex. A simple example:
@ -403,8 +403,8 @@ An assignment doesn't even have to save the expression into memory, although the
```
byte keypress
keypress = ^$C000 ; read keyboard
^$C010 ; read keyboard strobe, throw away value
keypress = ^$C000 // read keyboard
^$C010 // read keyboard strobe, throw away value
```
#### Control Flow
@ -424,19 +424,19 @@ The complex test case is handled with `when`. Basically a `if`, `elsifF`, `else`
```
when key
is 'A'
; handle A character
// handle A character
break
is 'B'
; handle B character
// handle B character
break
```
...
```
is 'Z'
; handle Z character
// handle Z character
break
otherwise
; Not a known key
// Not a known key
wend
```
With a little "Yoda-Speak", some fairly complex test can be made:
@ -448,13 +448,13 @@ byte a
when TRUE
is (a <= 10)
; 10 or less
// 10 or less
break
is (a > 10) AND (a < 20)
; between 10 and 20
// between 10 and 20
break
is (a >= 20)
; 20 or greater
// 20 or greater
wend
```
A `when` clause can fall-through to the following clause, just like C `switch` statements by leaving out the `break` at the end of a clause.
@ -463,11 +463,11 @@ A `when` clause can fall-through to the following clause, just like C `switch` s
Iteration over a range is handled with the `for`/`next` loop. When iterating from a smaller to larger value, the `to` construct is used; when iterating from larger to smaller, the `downto` construct is used.
```
for a = 1 to 10
; do something with a
// do something with a
next
for a = 10 downto 1
; do something else with a
// do something else with a
next
```
An optional stepping value can be used to change the default iteration step from 1 to something else. Always use a positive value; when iterating using `downto`, the step value will be subtracted from the current value.
@ -475,10 +475,10 @@ An optional stepping value can be used to change the default iteration step from
##### WHILE/LOOP
For loops that test at the top of the loop, use `while`. The loop will run zero or more times.
```
a = c ; Who knows what c could be
a = c // Who knows what c could be
while a < 10
; do something
a = b * 2 ; b is something special, I'm sure
// do something
a = b * 2 // b is something special, I'm sure
loop
```
##### REPEAT/UNTIL
@ -512,14 +512,14 @@ end
PLASMA always returns a value from a function, even if you don't supply one. Probably the easiest optimization to make in PLASMA is to cascade a return value if you don't care about the value you return. This only works if the last thing you do before returning from your routine is calling another definition. You would go from:
```
def mydef
; do some stuff
calldef(10) ; call some other def
// do some stuff
calldef(10) // call some other def
end
```
PLASMA will effectively add a RETURN 0 to the end of your function, as well as add code to ignore the result of `calldef(10)`. As long as you don't care about the return value from `mydef` or want to use its return as the return value fromyour function (cascade the return), you can save some code bytes with:
```
def mydef
; do some stuff
return calldef(10) ; call some other def
// do some stuff
return calldef(10) // call some other def
end
```

View File

@ -92,18 +92,18 @@ The PLASMA low level operations are defined as:
##PLASMA Compiler/Assembler
Although the low-level operations could easily by coded by hand, they were chosen to be an easy target for a simple compiler. Think along the lines of an advanced assembler or stripped down C compiler ( C--). Taking concepts from BASIC, Pascal, C and assembler, the PLASMA compiler is simple yet expressive. The syntax is line oriented; there is no statement delimiter except newline.
Although the low-level operations could easily by coded by hand, they were chosen to be an easy target for a simple compiler. Think along the lines of an advanced assembler or stripped down C compiler ( C--). Taking concepts from BASIC, Pascal, C and assembler, the PLASMA compiler is simple yet expressive. The syntax is line oriented. However, the ';' character can seperate multiple statements on a single line. It is generally considered bad form to have multiple statements per line unless they are very short.
Comments are allowed throughout the source, starting with the ; character. The rest of the line is ignored.
Comments are allowed throughout the source, starting with the // symbol. The rest of the line is ignored.
```
; Data and text buffer constants
// Data and text buffer constants
```
Hexadecimal constants are preceded with a $ to identify them as such.
```
$C030 ; Speaker address
$C030 // Speaker address
```
###Constants, Variables and Functions
@ -111,27 +111,27 @@ Hexadecimal constants are preceded with a $ to identify them as such.
The source code of a PLASMA module first defines imports, constants, variables and data. Constants must be initialized with a value. Variables can have sizes associated with them to declare storage space. Data can be declared with or without a variable name associated with it. Arrays, tables, strings and any predeclared data can be created and accessed in multiple ways.
```
;
; Import standard library functions.
;
//
// Import standard library functions.
//
import stdlib
predef putc, puts, getc, gets, cls, memcpy, memset, memclr
end
;
; Constants used for hardware and flags
;
//
// Constants used for hardware and flags
//
const speaker = $C030
const changed = 1
const insmode = 2
;
; Array declaration of screen row addresses
;
//
// Array declaration of screen row addresses
//
word txtscrn[] = $0400,$0480,$0500,$0580,$0600,$0680,$0700,$0780
word = $0428,$04A8,$0528,$05A8,$0628,$06A8,$0728,$07A8
word = $0450,$04D0,$0550,$05D0,$0650,$06D0,$0750,$07D0
;
; Misc global variables
;
//
// Misc global variables
//
byte flags = 0
word numlines = 0
byte cursx, cursy
@ -143,9 +143,9 @@ Variables can have optional brackets; empty brackets dont reserve any space f
Strings are defined like Pascal strings, a length byte followed by the string characters so they can be a maximum of 255 characters long. Strings can only appear in the variable definitions of a module. String constants cant be used in expressions or statements.
```
;
; An initialized string of 64 characters
;
//
// An initialized string of 64 characters
//
byte txtfile[64] = "UNTITLED"
```
@ -195,7 +195,7 @@ Functions with parameters or expressions to be used as a function address to cal
word keyin
byte key
keyin = @keyin2plus ; address-of keyin2plus function
keyin = @keyin2plus // address-of keyin2plus function
key = keyin()
```
@ -206,7 +206,7 @@ Expressions are algebraic. Data is free-form, but all operations on the evaluat
```
const speaker=$C030
^speaker ; click speaker
^speaker // click speaker
close(refnum)
```
@ -257,11 +257,11 @@ Control structures affect the flow of control through the program. There are co
```
if ^pushbttn3 < 128
if key == $C0
key = $D0 ; P
key = $D0 // P
elsif key == $DD
key = $CD ; M
key = $CD // M
elsif key == $DE
key = $CE ; N
key = $CE // N
fin
else
key = key | $E0
@ -346,7 +346,7 @@ romcall(aReg, xReg, yReg, statusReg, addr) returns a pointer to a four byte stru
const xreg = 1
const getlin = $FD6A
numchars = (romcall(0, 0, 0, 0, getlin)).xreg ; return char count in X reg
numchars = (romcall(0, 0, 0, 0, getlin)).xreg // return char count in X reg
```
syscall(cmd, params) calls ProDOS, returning the status value.
@ -367,8 +367,8 @@ syscall(cmd, params) calls ProDOS, returning the status value.
putc(char), puts(string), home, gotoxy(x,y), getc() and gets() are other handy utility routines for interacting with the console.
```
putc('.')
byte okstr[] = "OK"
putc('.')
puts(@okstr)
```
@ -376,7 +376,7 @@ memset(addr, len, val) will fill memory with a 16 bit value. memcpy(dstaddr, sr
```
byte nullstr[] = ""
memset(strlinbuf, maxfill * 2, @nullstr) ; fill line buff with pointer to null string
memset(strlinbuf, maxfill * 2, @nullstr) // fill line buff with pointer to null string
memcpy(scrnptr, strptr + ofst + 1, numchars)
```
@ -384,7 +384,7 @@ memset(addr, len, val) will fill memory with a 16 bit value. memcpy(dstaddr, sr
###The Original PLASMA
The original design concept was to create an efficient, flexible, and expressive environment for building applications directly on the Apple II. Choosing a stack based architecture was easy after much experience with other stack based implementations. It also makes the compiler simple to implement. The first take on the stack architecture was to make it a very strict stack architecture in that everything had to be on the stack. The only opcode with operands was the CONSTANT opcode. This allowed for a very small bytecode interpreter and a very easy compile target. However, only when adding an opcode with operands that would greatly improved performance, native code generation or code size was it done. The opcode table grew slowly over time but still retains a small runtime interpreter with good native code density.
The VM was constructed such that code generation could ouput native 6502 code, threaded code into the opcode functions, or interpreted bytecodes. This gave a level of control over speed vs memory.
The VM was constructed such that code generation could output native 6502 code, threaded code into the opcode functions, or interpreted bytecodes. This gave a level of control over speed vs memory.
###The Lawless Legends PLASMA
This version of PLASMA has dispensed with the native/threaded/bytecode code generation from the original version to focus on code density and the ability to interpret bytecode from AUX memory, should it be available. By focussing on the bytecode interpreter, certain optimizations were implemented that weren't posssible when allowing for threaded/native code; the interpreted bytecode is now about the same performance as the directly threaded code.

View File

@ -4,37 +4,37 @@ const databuff = $2000
const MODADDR = $1000
const symtbl = $0C00
const freemem = $0006
;
; System flags: memory allocator screen holes.
;
//
// System flags: memory allocator screen holes.
//
const restxt1 = $0001
const restxt2 = $0002
const reshgr1 = $0004
const reshgr2 = $0008
const resxhgr1 = $0010
const resxhgr2 = $0020
;
; Pedefined functions.
;
//
// Pedefined functions.
//
predef syscall, call
predef crout, cout, prstr, cin, rdstr
predef markheap, allocheap, allocalignheap, releaseheap, availheap
predef memset, memcpy
predef uword_isgt, uword_isge, uword_islt, uword_isle
predef loadmod, execmod, lookupstrmod
;
; System variable.
;
word version = $0010 ; 00.10
//
// System variable.
//
word version = $0010 // 00.10
word systemflags = 0
word heap
word xheap = $0800
word lastsym = symtbl
byte perr
word cmdptr
;
; Standard Library exported functions.
;
//
// Standard Library exported functions.
//
byte stdlibstr[] = "STDLIB"
byte machidstr[] = "MACHID"
byte sysstr[] = "SYSCALL"
@ -81,9 +81,9 @@ word = @modadrstr, @lookupstrmod
word = @machidstr, MACHID
word = 0
word stdlibsym = @exports
;
; String pool.
;
//
// String pool.
//
byte autorun[] = "AUTORUN"
byte verstr[] = "PLASMA "
byte freestr[] = "MEM FREE:$"
@ -91,14 +91,14 @@ byte errorstr[] = "ERR:$"
byte okstr[] = "OK"
byte huhstr[] = "?\n"
byte prefix[32] = ""
;
; Utility functions
;
;asm equates included from cmdstub.s
;
; CALL PRODOS
; SYSCALL(CMD, PARAMS)
;
//
// Utility functions
//
//asm equates included from cmdstub.s
//
// CALL PRODOS
// SYSCALL(CMD, PARAMS)
//
asm syscall
LDA ESTKL,X
LDY ESTKH,X
@ -115,10 +115,10 @@ PARAMS: !WORD 0000
STY ESTKH,X
RTS
end
;
; CALL 6502 ROUTINE
; CALL(ADDR, AREG, XREG, YREG, STATUS)
;
//
// CALL 6502 ROUTINE
// CALL(ADDR, AREG, XREG, YREG, STATUS)
//
asm call
REGVALS = SRC
PHP
@ -159,9 +159,9 @@ REGVALS = SRC
RTS
JMPTMP JMP (TMP)
end
;
; CALL LOADED SYSTEM PROGRAM
;
//
// CALL LOADED SYSTEM PROGRAM
//
asm exec
LDX #$00
STX IFPL
@ -173,19 +173,19 @@ asm exec
BIT ROMEN
JMP $2000
end
;
; EXIT
;
//
// EXIT
//
asm reboot
BIT ROMEN
DEC $03F4 ; INVALIDATE POWER-UP BYTE
JMP ($FFFC) ; RESET
end
;
; SET MEMORY TO VALUE
; MEMSET(ADDR, SIZE, VALUE)
; With optimizations from Peter Ferrie
;
//
// SET MEMORY TO VALUE
// MEMSET(ADDR, SIZE, VALUE)
// With optimizations from Peter Ferrie
//
asm memset
LDY #$00
LDA ESTKL+2,X
@ -212,10 +212,10 @@ SETMEX INX
INX
RTS
end
;
; COPY MEMORY
; MEMCPY(DSTADDR, SRCADDR, SIZE)
;
//
// COPY MEMORY
// MEMCPY(DSTADDR, SRCADDR, SIZE)
//
asm memcpy
INX
INX
@ -285,11 +285,11 @@ REVCPYLP LDA (SRC),Y
BNE REVCPYLP
CPYMEX RTS
end
;
; COPY FROM MAIN MEM TO AUX MEM.
;
; MEMXCPY(DST, SRC, SIZE)
;
//
// COPY FROM MAIN MEM TO AUX MEM.
//
// MEMXCPY(DST, SRC, SIZE)
//
asm memxcpy
LDA ESTKL+1,X
STA $3C
@ -318,12 +318,12 @@ asm crout
DEX
LDA #$8D
STA ESTKL,X
; FALL THROUGH TO COUT
// FALL THROUGH TO COUT
end
;
; CHAR OUT
; COUT(CHAR)
;
//
// CHAR OUT
// COUT(CHAR)
//
asm cout
LDA ESTKL,X
BIT $BF98
@ -335,10 +335,10 @@ asm cout
BIT LCRDEN+LCBNK2
RTS
end
;
; CHAR IN
; RDKEY()
;
//
// CHAR IN
// RDKEY()
//
asm cin
BIT ROMEN
JSR $FD0C
@ -349,10 +349,10 @@ asm cin
STY ESTKH,X
RTS
end
;
; PRINT STRING
; PRSTR(STR)
;
//
// PRINT STRING
// PRSTR(STR)
//
asm prstr
LDY #$00
LDA ESTKL,X
@ -375,9 +375,9 @@ asm prstr
BIT LCRDEN+LCBNK2
++ RTS
end
;
; PRINT BYTE
;
//
// PRINT BYTE
//
asm prbyte
LDA ESTKL,X
STX ESP
@ -387,9 +387,9 @@ asm prbyte
BIT LCRDEN+LCBNK2
RTS
end
;
; PRINT WORD
;
//
// PRINT WORD
//
asm prword
STX ESP
TXA
@ -402,10 +402,10 @@ asm prword
BIT LCRDEN+LCBNK2
RTS
end
;
; READ STRING
; STR = RDSTR(PROMPTCHAR)
;
//
// READ STRING
// STR = RDSTR(PROMPTCHAR)
//
asm rdstr
LDA ESTKL,X
STA $33
@ -476,23 +476,23 @@ asm uword_islt
INX
RTS
end
;
; Utility routines.
;
; A DCI string is one that has the high bit set for every character except the last.
; More efficient than C or Pascal strings.
;
;def dcitos(dci, str)
; byte len, c
; len = 0
; repeat
; c = (dci).[len]
; len = len + 1
; (str).[len] = c & $7F
; until !(c & $80)
; ^str = len
; return len
;end
//
// Utility routines.
//
// A DCI string is one that has the high bit set for every character except the last.
// More efficient than C or Pascal strings.
//
//def dcitos(dci, str)
// byte len, c
// len = 0
// repeat
// c = (dci).[len]
// len = len + 1
// (str).[len] = c & $7F
// until !(c & $80)
// ^str = len
// return len
//end
asm dcitos
LDA ESTKL,X
STA DSTL
@ -517,22 +517,22 @@ asm dcitos
STY ESTKH,X
RTS
end
;def stodci(str, dci)
; byte len, c
; len = ^str
; if len == 0
; return
; fin
; c = toupper((str).[len]) & $7F
; len = len - 1
; (dci).[len] = c
; while len
; c = toupper((str).[len]) | $80
; len = len - 1
; (dci).[len] = c
; loop
; return ^str
;end
//def stodci(str, dci)
// byte len, c
// len = ^str
// if len == 0
// return
// fin
// c = toupper((str).[len]) & $7F
// len = len - 1
// (dci).[len] = c
// while len
// c = toupper((str).[len]) | $80
// len = len - 1
// (dci).[len] = c
// loop
// return ^str
//end
asm stodci
LDA ESTKL,X
STA DSTL
@ -572,22 +572,22 @@ TOUPR AND #$7F
+ STA ESTKL,X
RTS
end
;
; Module symbols are entered into the symbol table
; pre-pended with a '#' to differentiate them
; from normal symbols.
;
;def modtosym(mod, dci)
; byte len, c
; (dci).0 = '#'|$80
; len = 0
; repeat
; c = (mod).[len]
; len = len + 1
; (dci).[len] = c
; until !(c & $80)
; return dci
;end
//
// Module symbols are entered into the symbol table
// pre-pended with a '#' to differentiate them
// from normal symbols.
//
//def modtosym(mod, dci)
// byte len, c
// (dci).0 = '#'|$80
// len = 0
// repeat
// c = (mod).[len]
// len = len + 1
// (dci).[len] = c
// until !(c & $80)
// return dci
//end
asm modtosym
LDA ESTKL+1,X
STA SRCL
@ -609,26 +609,26 @@ asm modtosym
BCS -
RTS
end
;
; Lookup routines.
;
;def lookuptbl(dci, tbl)
; word match
; while ^tbl
; match = dci
; while ^tbl == ^match
; if !(^tbl & $80)
; return (tbl):1
; fin
; tbl = tbl + 1
; match = match + 1
; loop
; while (^tbl & $80)
; tbl = tbl + 1
; loop
; tbl = tbl + 3
; loop
; return 0
//
// Lookup routines.
//
//def lookuptbl(dci, tbl)
// word match
// while ^tbl
// match = dci
// while ^tbl == ^match
// if !(^tbl & $80)
// return (tbl):1
// fin
// tbl = tbl + 1
// match = match + 1
// loop
// while (^tbl & $80)
// tbl = tbl + 1
// loop
// tbl = tbl + 3
// loop
// return 0
asm lookuptbl
LDA ESTKL,X
STA DSTL
@ -670,9 +670,9 @@ asm lookuptbl
INC DSTH
BCS -
end
;
; ProDOS routines
;
//
// ProDOS routines
//
def getpfx(path)
byte params[3]
@ -719,9 +719,9 @@ def read(refnum, buff, len)
perr = syscall($CA, @params)
return params:6
end
;
; Heap routines.
;
//
// Heap routines.
//
def availheap
byte fp
return @fp - heap
@ -761,11 +761,11 @@ def allocalignheap(size, pow2, freeaddr)
return addr
end
def markheap
return heap;
return heap//
end
def releaseheap(newheap)
heap = newheap;
return @newheap - heap;
heap = newheap//
return @newheap - heap//
end
def allocxheap(size)
word xaddr
@ -800,9 +800,9 @@ def allocxheap(size)
fin
return xaddr
end
;
; Symbol table routines.
;
//
// Symbol table routines.
//
def lookupsym(sym)
return lookuptbl(sym, symtbl)
end
@ -817,9 +817,9 @@ def addsym(sym, addr)
lastsym = lastsym + 3
^lastsym = 0
end
;
; Module routines.
;
//
// Module routines.
//
def lookupmod(mod)
byte dci[17]
return lookuptbl(modtosym(mod, @dci), symtbl)
@ -858,13 +858,13 @@ def adddef(bank, addr, deflast)
defentry = *deflast
*deflast = defentry + 5
if bank
defentry=>1 = $03DC ; JSR $03DC (AUX MEM INTERP)
defentry=>1 = $03DC // JSR $03DC (AUX MEM INTERP)
else
defentry=>1 = $03D6 ; JSR $03D6 (MAIN MEM INTERP)
defentry=>1 = $03D6 // JSR $03D6 (MAIN MEM INTERP)
fin
defentry->0 = $20
defentry=>3 = addr
defentry->5 = 0 ; NULL out next entry
defentry->5 = 0 // NULL out next entry
return defentry
end
def lookupdef(addr, deftbl)
@ -883,9 +883,9 @@ def loadmod(mod)
word moddep, rld, esd, sym
byte defbank, str[16], filename[64]
byte header[128]
;
; Read the RELocatable module header (first 128 bytes)
;
//
// Read the RELocatable module header (first 128 bytes)
//
dcitos(mod, @filename)
refnum = open(@filename, iobuffer)
if refnum > 0
@ -894,18 +894,18 @@ def loadmod(mod)
moddep = @header.1
defofst = modsize
init = 0
if rdlen > 4 and header:2 == $DA7E ; DAVE = magic number :-)
;
; This is an EXTended RELocatable (data+bytecode) module.
;
if rdlen > 4 and header:2 == $DA7E // DAVE = magic number :-)
//
// This is an EXTended RELocatable (data+bytecode) module.
//
systemflags = header:4 | systemflags
defofst = header:6
defcnt = header:8
init = header:10
moddep = @header.12
;
; Load module dependencies.
;
//
// Load module dependencies.
//
while ^moddep
if !lookupmod(moddep)
close(refnum)
@ -916,57 +916,57 @@ def loadmod(mod)
fin
moddep = moddep + dcitos(moddep, @str)
loop
;
; Init def table.
;
//
// Init def table.
//
deftbl = allocheap(defcnt * 5 + 1)
deflast = deftbl
^deflast = 0
if !refnum
;
; Reset read pointer.
;
//
// Reset read pointer.
//
refnum = open(@filename, iobuffer)
rdlen = read(refnum, @header, 128)
fin
fin
;
; Alloc heap space for relocated module (data + bytecode).
;
//
// Alloc heap space for relocated module (data + bytecode).
//
moddep = moddep + 1
modfix = moddep - @header.2 ; Adjust to skip header
modfix = moddep - @header.2 // Adjust to skip header
modsize = modsize - modfix
rdlen = rdlen - modfix - 2
modaddr = allocheap(modsize)
memcpy(modaddr, moddep, rdlen)
;
; Read in remainder of module into memory for fixups.
;
addr = modaddr;
//
// Read in remainder of module into memory for fixups.
//
addr = modaddr//
repeat
addr = addr + rdlen
rdlen = read(refnum, addr, 4096)
until rdlen <= 0
close(refnum)
;
; Add module to symbol table.
;
//
// Add module to symbol table.
//
addmod(mod, modaddr)
;
; Apply all fixups and symbol import/export.
;
//
// Apply all fixups and symbol import/export.
//
modfix = modaddr - modfix
bytecode = defofst + modfix - MODADDR
modend = modaddr + modsize
rld = modend ; Re-Locatable Directory
esd = rld ; Extern+Entry Symbol Directory
while ^esd ; Scan to end of ESD
rld = modend // Re-Locatable Directory
esd = rld // Extern+Entry Symbol Directory
while ^esd // Scan to end of ESD
esd = esd + 4
loop
esd = esd + 1
;
; Locate bytecode defs in appropriate bank.
;
//
// Locate bytecode defs in appropriate bank.
//
if ^MACHID & $30 == $30
defbank = 1
defaddr = allocxheap(rld - bytecode)
@ -975,58 +975,58 @@ def loadmod(mod)
defbank = 0
defaddr = bytecode
fin
;
; Run through the Re-Location Dictionary.
;
//
// Run through the Re-Location Dictionary.
//
while ^rld
if ^rld == $02
;
; This is a bytcode def entry - add it to the def directory.
;
//
// This is a bytcode def entry - add it to the def directory.
//
adddef(defbank, rld=>1 - defofst + defaddr, @deflast)
else
addr = rld=>1 + modfix
if uword_isge(addr, modaddr) ; Skip fixups to header
if ^rld & $80 ; WORD sized fixup.
if uword_isge(addr, modaddr) // Skip fixups to header
if ^rld & $80 // WORD sized fixup.
fixup = *addr
else ; BYTE sized fixup.
else // BYTE sized fixup.
fixup = ^addr
fin
if ^rld & $10 ; EXTERN reference.
if ^rld & $10 // EXTERN reference.
fixup = fixup + lookupextern(esd, rld->3)
else ; INTERN fixup.
else // INTERN fixup.
fixup = fixup + modfix - MODADDR
if uword_isge(fixup, bytecode)
;
; Bytecode address - replace with call def directory.
;
//
// Bytecode address - replace with call def directory.
//
fixup = lookupdef(fixup - bytecode + defaddr, deftbl)
fin
fin
if ^rld & $80 ; WORD sized fixup.
if ^rld & $80 // WORD sized fixup.
*addr = fixup
else ; BYTE sized fixup.
else // BYTE sized fixup.
^addr = fixup
fin
fin
fin
rld = rld + 4
loop
;
; Run through the External/Entry Symbol Directory.
;
//
// Run through the External/Entry Symbol Directory.
//
while ^esd
sym = esd
esd = esd + dcitos(esd, @str)
if ^esd & $08
;
; EXPORT symbol - add it to the global symbol table.
;
//
// EXPORT symbol - add it to the global symbol table.
//
addr = esd=>1 + modfix - MODADDR
if uword_isge(addr, bytecode)
;
; Use the def directory address for bytecode.
;
//
// Use the def directory address for bytecode.
//
addr = lookupdef(addr - bytecode + defaddr, deftbl)
fin
addsym(sym, addr)
@ -1034,18 +1034,18 @@ def loadmod(mod)
esd = esd + 3
loop
if defbank
;
; Move bytecode to AUX bank.
;
//
// Move bytecode to AUX bank.
//
memxcpy(defaddr, bytecode, modsize - (bytecode - modaddr))
fin
fin
if perr
return -perr
fin
;
; Call init routine if it exists.
;
//
// Call init routine if it exists.
//
if init
fixup = adddef(defbank, init - defofst + defaddr, @deflast)()
if defbank
@ -1056,15 +1056,15 @@ def loadmod(mod)
else
fixup = 0
fin
;
; Free up the end-of-module in main memory.
;
//
// Free up the end-of-module in main memory.
//
releaseheap(modend)
return fixup
end
;
; Command mode
;
//
// Command mode
//
def volumes
byte params[4]
word strbuf
@ -1120,7 +1120,7 @@ def catalog(optpath)
len = type & $0F
^entry = len
prstr(entry)
if type & $F0 == $D0 ; Is it a directory?
if type & $F0 == $D0 // Is it a directory?
cout('/')
len = len + 1
elsif entry->$10 == $FF
@ -1186,14 +1186,14 @@ def parsecmd(strptr)
return cmd
end
def resetmemfiles
;
; Close all files
;
//
// Close all files
//
^$BFD8 = 0
close(0)
;
; Set memory bitmap
;
//
// Set memory bitmap
//
memset($BF58, 24, 0)
^$BF58 = $CF
^$BF6F = $01
@ -1240,13 +1240,13 @@ def execmod(modfile)
heap = saveheap
fin
end
;
; Get heap start.
;
//
// Get heap start.
//
heap = *freemem
;
; Init symbol table.
;
//
// Init symbol table.
//
stodci(@stdlibstr, heap)
addmod(heap, @version)
while *stdlibsym
@ -1254,15 +1254,15 @@ while *stdlibsym
addsym(heap, stdlibsym=>2)
stdlibsym = stdlibsym + 4
loop
;
; Try to run autorun module.
;
//
// Try to run autorun module.
//
resetmemfiles()
execmod(@autorun)
perr = 0
;
; Print some startup info.
;
//
// Print some startup info.
//
prstr(@verstr)
prbyte(version.1)
cout('.')

View File

@ -4,7 +4,7 @@
#include "tokens.h"
#include "symbols.h"
char *statement, *scanpos, *tokenstr;
char *statement, *tokenstr, *scanpos = "";
t_token scantoken, prevtoken;
int tokenlen;
long constval;
@ -22,28 +22,28 @@ t_token keywords[] = {
ENDCASE_TOKEN, 'W', 'E', 'N', 'D',
FOR_TOKEN, 'F', 'O', 'R',
TO_TOKEN, 'T', 'O',
DOWNTO_TOKEN, 'D', 'O', 'W', 'N', 'T', 'O',
DOWNTO_TOKEN, 'D', 'O', 'W', 'N', 'T', 'O',
STEP_TOKEN, 'S', 'T', 'E', 'P',
NEXT_TOKEN, 'N', 'E', 'X', 'T',
REPEAT_TOKEN, 'R', 'E', 'P', 'E', 'A', 'T',
UNTIL_TOKEN, 'U', 'N', 'T', 'I', 'L',
BREAK_TOKEN, 'B', 'R', 'E', 'A', 'K',
UNTIL_TOKEN, 'U', 'N', 'T', 'I', 'L',
BREAK_TOKEN, 'B', 'R', 'E', 'A', 'K',
ASM_TOKEN, 'A', 'S', 'M',
DEF_TOKEN, 'D', 'E', 'F',
EXPORT_TOKEN, 'E', 'X', 'P', 'O', 'R', 'T',
IMPORT_TOKEN, 'I', 'M', 'P', 'O', 'R', 'T',
EXPORT_TOKEN, 'E', 'X', 'P', 'O', 'R', 'T',
IMPORT_TOKEN, 'I', 'M', 'P', 'O', 'R', 'T',
RETURN_TOKEN, 'R', 'E', 'T', 'U', 'R', 'N',
END_TOKEN, 'E', 'N', 'D',
EXIT_TOKEN, 'E', 'X', 'I', 'T',
DONE_TOKEN, 'D', 'O', 'N', 'E',
LOGIC_NOT_TOKEN, 'N', 'O', 'T',
LOGIC_AND_TOKEN, 'A', 'N', 'D',
LOGIC_OR_TOKEN, 'O', 'R',
LOGIC_OR_TOKEN, 'O', 'R',
BYTE_TOKEN, 'B', 'Y', 'T', 'E',
WORD_TOKEN, 'W', 'O', 'R', 'D',
CONST_TOKEN, 'C', 'O', 'N', 'S', 'T',
PREDEF_TOKEN, 'P', 'R', 'E', 'D', 'E', 'F',
SYSFLAGS_TOKEN, 'S', 'Y', 'S', 'F', 'L', 'A', 'G', 'S',
SYSFLAGS_TOKEN, 'S', 'Y', 'S', 'F', 'L', 'A', 'G', 'S',
EOL_TOKEN
};
@ -333,6 +333,15 @@ t_token scan(void)
scanpos++;
}
break;
case '/':
if (scanpos[1] == '/')
scantoken = EOL_TOKEN;
else
{
scantoken = DIV_TOKEN;
scanpos++;
}
break;
default:
/*
* Simple single character tokens.
@ -363,12 +372,21 @@ int scan_lookahead(void)
char inputline[512];
int next_line(void)
{
gets(inputline);
lineno++;
statement = inputline;
scanpos = inputline;
scantoken = EOL_TOKEN;
scan();
printf("; %03d: %s\n", lineno, inputline);
if (*scanpos == ';')
{
statement = ++scanpos;
scantoken = EOL_TOKEN;
scan();
}
else
{
gets(inputline);
lineno++;
statement = inputline;
scanpos = inputline;
scantoken = EOL_TOKEN;
scan();
printf("; %03d: %s\n", lineno, inputline);
}
return (1);
}

View File

@ -6,9 +6,9 @@ import stdlib
predef isugt, isuge, isult, isule
predef load, exec
word MACHID, sysvars
;
; System flags: memory allocator screen holes.
;
//
// System flags: memory allocator screen holes.
//
const restxt1 = $0001
const restxt2 = $0002
const reshgr1 = $0004

View File

@ -1,13 +1,13 @@
;
; Include all imported modules and their data/functions.
;
//
// Include all imported modules and their data/functions.
//
include(stdlib.plh)
include(testlib.plh)
;
; Declare all global variables for this module.
;
//
// Declare all global variables for this module.
//
byte hello[] = "Hello, Apple "
byte a1[] = "1"
@ -20,9 +20,9 @@ word struct[] = 1, 10, 100, 1000, 10000
word ptr
byte spaces[] = " "
;
; Define functions.
;
//
// Define functions.
//
def tens(start)
word i

View File

@ -1,21 +1,21 @@
;
; Include all imported modules and their data/functions.
;
//
// Include all imported modules and their data/functions.
//
include(stdlib.plh)
;
; Module data.
;
//
// Module data.
//
predef puti, puth, putln
export word print[] = @puti, @puth, @putln, @puts, @putc
byte valstr[] = '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
byte loadstr[] = "testlib loaded!"
;
; Define functions.
;
//
// Define functions.
//
def puth(h)
putc('$')
@ -26,10 +26,7 @@ def puth(h)
end
export def puti(i)
if i < 0
putc('-')
i = -i
fin
if i < 0; putc('-'); i = -i; fin
if i < 10
putc(i + '0')
else

View File

@ -38,6 +38,10 @@ NW_MAP_LOC=$52
NE_MAP_LOC=$54
SW_MAP_LOC=$56
SE_MAP_LOC=$58
NW_TILESET_LOC=$70
NE_TILESET_LOC=$72
SW_TILESET_LOC=$74
SE_TILESET_LOC=$76
; Map section IDs (255 = not loaded)
NOT_LOADED=$FF
NW_MAP_ID=$5A
@ -50,18 +54,21 @@ EAST =1
SOUTH =2
WEST =3
;-- Variables used in drawing
;-- Variables used in drawing which can not be changed by the inner drawing loop
DRAW_X_START = $5E ; Starting column being drawn (between 0 and VIEWPORT_WIDTH)
DRAW_Y_START = $5F ; Starting row being drawn (between 0 and VIEWPORT_WIDTH)
DRAW_WIDTH = $62 ; Number of columns to draw for current section (cannot be destroyed by drawing loop)
DRAW_HEIGHT = $63 ; Number of rows to draw for current section (cannot be destroyed by drawing loop)
DRAW_SECTION = $64 ; Location of section data being drawn
TILE_BASE = $6B ; Location of tile data
;-- These variables are set in the outer draw section but can be destroyed by the inner routine
SECTION_X_START = $60 ; X Offset relative to current section being drawn
SECTION_Y_START = $61 ; Y Offset relative to current section being drawn
DRAW_WIDTH = $62 ; Number of columns to draw for current section (cannot be destroyed by drawing loop)
DRAW_HEIGTH = $63 ; Number of rows to draw for current section (cannot be destroyed by drawing loop)
DRAW_SECTION = $64 ; Location of section data being drawn
X_COUNTER = $66 ; Loop counter used during drawing
Y_COUNTER = $67 ; Loop counter used during drawing
ROW_LOCATION = $68 ; Used for pointing at row offset in map data
Y_LOC = $68 ; Current row being drawn (between 0 and VIEWPORT_WIDTH)
ROW_LOCATION = $69 ; Used for pointing at row offset in map data
TILE_SOURCE = $6D ; Location of tile data
; >> INIT (reset map drawing vars)
INIT
LDA #NOT_LOADED
@ -76,12 +83,12 @@ INIT
;----------------------------------------------------------------------
; >> START LOADING MAP SECTIONS
START_LOAD
START_MAP_LOAD
LDX #0
LDA #START_LOAD
JMP mainLoader
!macro startLoad {
JSR START_LOAD
JSR START_MAP_LOAD
}
;----------------------------------------------------------------------
@ -113,17 +120,17 @@ LOAD_SECTION
;----------------------------------------------------------------------
; >> FINISH LOADING MAP SECTIONS
FINISH_LOAD
FINISH_MAP_LOAD
LDX #0 ; 1 to keep open for next load, 0 for close so you can flip to HGR page 2
LDA #FINISH_LOAD
JMP mainLoader
!macro finishLoad {
JSR FINISH_LOAD
JSR FINISH_MAP_LOAD
}
;----------------------------------------------------------------------
; >> RELEASE MAP SECTION
!macro releaseMapSection ptr {
; >> RELEASE MAP SECTION OR TILESET
!macro freeResource ptr {
; --> free up unused resource
LDX ptr
LDY ptr+1
@ -133,6 +140,18 @@ FINISH_LOAD
;----------------------------------------------------------------------
; >> LOAD TILES
; Load tile resource (A = Resource ID)
LOAD_TILESET
TAY
LDX #RES_TYPE_TILESET
LDA #QUEUE_LOAD
JMP mainLoader
!macro loadTileset mapData, ptr {
LDY #4
LDA (mapData),Y
JSR LOAD_TILESET
STX ptr
STY ptr+1
}
;----------------------------------------------------------------------
; >> MOVE NORTH
; Check for boundary
@ -170,8 +189,8 @@ SET_XY
; >> TRIGGER SCRIPT AT TILE (X,Y = Coordinates in section)
;----------------------------------------------------------------------
!macro move_word from, to {
!move_byte from, to
!move_byte from+1, to+1
+move_byte from, to
+move_byte from+1, to+1
}
!macro move_byte from, to {
@ -179,110 +198,139 @@ SET_XY
STX to
}
FREE_ALL_TILES
+freeResource NW_TILESET_LOC
+freeResource NE_TILESET_LOC
+freeResource SW_TILESET_LOC
+freeResource SE_TILESET_LOC
RTS
!macro freeAllTiles {
JSR FREE_ALL_TILES
}
LOAD_ALL_TILES
+loadTileset NW_MAP_LOC, NW_TILESET_LOC
+loadTileset NE_MAP_LOC, NW_TILESET_LOC
+loadTileset SW_MAP_LOC, NW_TILESET_LOC
+loadTileset SE_MAP_LOC, NW_TILESET_LOC
RTS
!macro loadAllTiles {
JSR LOAD_ALL_TILES
}
; >> CROSS NORTH BOUNDARY (Load next section to the north)
!zone
CROSS_NORTH
!releaseMapSection SW_MAP_LOC
!releaseMapSection SE_MAP_LOC
+freeAllTiles
+freeResource SW_MAP_LOC
+freeResource SE_MAP_LOC
LDA REL_Y
CLC
ADC #SECTION_HEIGHT
STA REL_Y
!move_byte NW_MAP_ID, SW_MAP_ID
!move_word NW_MAP_LOC, SW_MAP_LOC
!move_byte NE_MAP_ID, SE_MAP_ID
!move_word NE_MAP_LOC, SE_MAP_LOC
+move_byte NW_MAP_ID, SW_MAP_ID
+move_word NW_MAP_LOC, SW_MAP_LOC
+move_byte NE_MAP_ID, SE_MAP_ID
+move_word NE_MAP_LOC, SE_MAP_LOC
; Get new NW section
!startLoad
LDA (SW_MAP_LOC)
+startLoad
LDY #00
LDA (SW_MAP_LOC),Y
STA NW_MAP_ID
!loadSection NW_MAP_LOC
+loadSection NW_MAP_LOC
; Get the new NE section
LDA (SE_MAP_LOC)
LDA (SE_MAP_LOC),Y
STA NE_MAP_ID
!loadSection NE_MAP_LOC
!finishLoad
+loadSection NE_MAP_LOC
+loadAllTiles
+finishLoad
RTS
;----------------------------------------------------------------------
; >> CROSS EAST BOUNDARY (Load next section to the east)
!zone
CROSS_EAST
!releaseMapSection NW_MAP_LOC
!releaseMapSection SW_MAP_LOC
+freeAllTiles
+freeResource NW_MAP_LOC
+freeResource SW_MAP_LOC
LDA REL_X
SEC
SBC #SECTION_WIDTH
STA REL_X
!move_byte NE_MAP_ID, NW_MAP_ID
!move_word NE_MAP_LOC, NW_MAP_LOC
!move_byte SE_MAP_ID, SW_MAP_ID
!move_word SE_MAP_LOC, SW_MAP_LOC
+move_byte NE_MAP_ID, NW_MAP_ID
+move_word NE_MAP_LOC, NW_MAP_LOC
+move_byte SE_MAP_ID, SW_MAP_ID
+move_word SE_MAP_LOC, SW_MAP_LOC
; Get new NE section
!startLoad
+startLoad
LDY #EAST
LDA (NW_MAP_LOC),Y
STA NE_MAP_ID
!loadSection NE_MAP_LOC
+loadSection NE_MAP_LOC
; Get the new SE section
LDY #EAST
LDA (SW_MAP_LOC),Y
STA SE_MAP_ID
!loadSection SE_MAP_LOC
!finishLoad
+loadSection SE_MAP_LOC
+loadAllTiles
+finishLoad
RTS
;----------------------------------------------------------------------
; >> CROSS SOUTH BOUNDARY (Load next section to the south)
!zone
CROSS_SOUTH
!releaseMapSection NW_MAP_LOC
!releaseMapSection NE_MAP_LOC
+freeAllTiles
+freeResource NW_MAP_LOC
+freeResource NE_MAP_LOC
LDA REL_Y
SEC
SBC #SECTION_HEIGHT
STA REL_Y
!move_byte SW_MAP_ID, NW_MAP_ID
!move_word SW_MAP_LOC, NW_MAP_LOC
!move_byte SE_MAP_ID, NE_MAP_ID
!move_word SE_MAP_LOC, NE_MAP_LOC
+move_byte SW_MAP_ID, NW_MAP_ID
+move_word SW_MAP_LOC, NW_MAP_LOC
+move_byte SE_MAP_ID, NE_MAP_ID
+move_word SE_MAP_LOC, NE_MAP_LOC
; Get new SW section
!startLoad
+startLoad
LDY #SOUTH
LDA (NW_MAP_LOC),Y
STA SW_MAP_ID
!loadSection SW_MAP_LOC
+loadSection SW_MAP_LOC
; Get the new SE section
LDY #SOUTH
LDA (NE_MAP_LOC),Y
STA SE_MAP_ID
!loadSection SE_MAP_LOC
!finishLoad
+loadSection SE_MAP_LOC
+loadAllTiles
+finishLoad
RTS
;----------------------------------------------------------------------
; >> CROSS WEST BOUNDARY (load next section to the west)
!zone
CROSS_WEST
!releaseMapSection NE_MAP_LOC
!releaseMapSection SE_MAP_LOC
+freeAllTiles
+freeResource NE_MAP_LOC
+freeResource SE_MAP_LOC
LDA REL_X
CLC
ADC #SECTION_WIDTH
STA REL_X
!move_byte NW_MAP_ID, NE_MAP_ID
!move_word NW_MAP_LOC, NE_MAP_LOC
!move_byte SW_MAP_ID, SE_MAP_ID
!move_word SW_MAP_LOC, SE_MAP_LOC
+move_byte NW_MAP_ID, NE_MAP_ID
+move_word NW_MAP_LOC, NE_MAP_LOC
+move_byte SW_MAP_ID, SE_MAP_ID
+move_word SW_MAP_LOC, SE_MAP_LOC
; Get new NW section
!startLoad
+startLoad
LDY #WEST
LDA (NE_MAP_LOC),Y
STA NW_MAP_ID
!loadSection NW_MAP_LOC
+loadSection NW_MAP_LOC
; Get the new SE section
LDY #WEST
LDA (SE_MAP_LOC),Y
STA SW_MAP_ID
!loadSection SW_MAP_LOC
!finishLoad
+loadSection SW_MAP_LOC
+loadAllTiles
+finishLoad
RTS
;----------------------------------------------------------------------
; >> SET PLAYER TILE (A = tile)
@ -291,7 +339,7 @@ CROSS_WEST
;----------------------------------------------------------------------
; >> DRAW
!zone draw
!macro drawMapSection ptr, deltaX, deltaY {
!macro drawMapSection mapPtr, tilesetPtr, deltaX, deltaY {
; Determine X1 and X2 bounds for what is being drawn
LDA REL_X
SEC
@ -304,7 +352,7 @@ CROSS_WEST
CLC
ADC #VIEWPORT_WIDTH
CMP #SECTION_WIDTH
BLT .11
BMI .11
LDA #SECTION_WIDTH
.11
SEC
@ -322,14 +370,15 @@ CROSS_WEST
CLC
ADC #VIEWPORT_HEIGHT
CMP #SECTION_HEIGHT
BLT .21
BMI .21
LDA #SECTION_HEIGHT
.21
SEC
SBC SECTION_Y_START
STA DRAW_HEIGHT
!move_word ptr, DRAW_SECTION
jsr mainDraw
+move_word mapPtr, DRAW_SECTION
+move_word tilesetPtr, TILE_BASE
JSR MainDraw
}
DRAW
@ -340,20 +389,22 @@ DRAW
.checkNorthQuads
LDA REL_Y
CMP SECTION_HEIGHT+VIEWPORT_VERT_PAD
BGE .checkSouthQuads
BMI .checkNWQuad
JMP .checkSouthQuads
.checkNWQuad
; Check for NW quadrant area
LDA REL_X
CMP SECTION_WIDTH+VIEWPORT_HORIZ_PAD
BGE .checkNEQuad
!drawMapSection NW_MAP_LOC, 0, 0
BPL .checkNEQuad
+drawMapSection NW_MAP_LOC, NW_TILESET_LOC, 0, 0
; Check for NE quadrant area
.checkNEQuad
LDA REL_X
CMP #VIEWPORT_HORIZ_PAD+1
BLT .finishTopQuads
BMI .finishTopQuads
LDA DRAW_WIDTH
STA DRAW_X_START
!drawMapSection NE_MAP_LOC, SECTION_WIDTH, 0
+drawMapSection NE_MAP_LOC, NE_TILESET_LOC, SECTION_WIDTH, 0
.finishTopQuads
;Update Y start for bottom quadrants
LDA DRAW_HEIGHT
@ -365,21 +416,21 @@ DRAW
; Check for SW quadrant area
LDA REL_X
CMP SECTION_WIDTH+VIEWPORT_HORIZ_PAD
BGE .checkSEQuad
!drawMapSection SW_MAP_LOC, 0, SECTION_HEIGHT
BPL .checkSEQuad
+drawMapSection SW_MAP_LOC, SW_TILESET_LOC, 0, SECTION_HEIGHT
.checkSEQuad
; Check for SE quadrand area
LDA REL_X
CMP #VIEWPORT_HORIZ_PAD+1
BGE .drawSEQuad
BPL .drawSEQuad
RTS
.drawSEQuad
LDA DRAW_WIDTH
STA DRAW_X_START
!drawMapSection SE_MAP_LOC, SECTION_WIDTH, SECTION_HEIGHT
+drawMapSection SE_MAP_LOC, SE_TILESET_LOC, SECTION_WIDTH, SECTION_HEIGHT
RTS
.mainDraw
MainDraw
;----- Tracking visible tile data -----
;There are a total of 512 screen holes in a hires page located in xx78-xx7F and xxF8-xxFF
;We only need 81 screen holes to track the 9x9 visible area. So to do this a little translation is needed
@ -399,9 +450,14 @@ DRAW
COL_OFFSET = 2
ROW_OFFSET = 3
LDA DRAW_HEIGHT
STA Y_COUNTER
LDA DRAW_Y_START
STA Y_LOC
.rowLoop
; Identify start of map data (upper left)
; Self-modifying code: Update all the STA statements in the drawTile section
LDY DRAW_Y_START
LDY Y_LOC
LDA tblHGRl+ROW_OFFSET, Y
ADC #COL_OFFSET
TAX
@ -431,7 +487,7 @@ ROW_OFFSET = 3
CLC
LDA SECTION_Y_START ;row * 2
ASL
ADC #HEADER_LENGTH
ADC #HEADER_LENGTH ; +6
ADC SECTION_X_START
STA ROW_LOCATION
LDA SECTION_Y_START ; row * 4
@ -449,24 +505,15 @@ ROW_OFFSET = 3
LDA DRAW_SECTION + 1
ADC #$00 ; This is a short way for handling carry without a branch
STA ROW_LOCATION + 1
LDA DRAW_SECTION
LDA DRAW_SECTION ; DRAW_SECTION is done at the very end in case it causes an overflow
ADC ROW_LOCATION
STA ROW_LOCATION
; Handle carry if needed
BCC .doneCalculatingLocation
INC ROW_LOCATION + 1
.doneCalculatingLocation
CLC
LDA SECTION_Y_START
ASL
ADC ROW_LOCATION
STA ROW_LOCATION
LDA SECTION_Y_START
ASL
ASL
ADC ROW_LOCATION
STA ROW_LOCATION
LDA DRAW_WIDTH
STA X_COUNTER
LDX DRAW_X_START
; Display row of tiles
.next_col
@ -479,15 +526,15 @@ ROW_OFFSET = 3
ASL
ASL
STA TILE_SOURCE
LDA TILE_BASE
LDA TILE_BASE + 1
ADC #$00
STA TILE_SOURCE+1
LDA TILE_BASE
ADC TILE_SOURCE
STA TILE_SOURCE
BCC .doenCalculatingTileLocation
BCC .doneCalculatingTileLocation
INC TILE_SOURCE+1
.doenCalculatingTileLocation
.doneCalculatingTileLocation
; Is there a NPC there?
; No, use map tile
; Yes, use NPC tile
@ -502,27 +549,33 @@ ROW_OFFSET = 3
ASL
TAX
.drawTile !for row, 16 {
.0 LDA (TILE_SOURCE),Y
.2 STA DRAW_ROW, X
.5 INY
.6 INX
.7 LDA (TILE_SOURCE),Y
.9 STA DRAW_ROW, X
!if row < 15 {
.12 INY
.13 DEX
}
LDA (TILE_SOURCE),Y ;0
STA $2000, X ;2
INY ;5
INX ;6
LDA (TILE_SOURCE),Y ;7
STA $2000, X ;9
!if row < 15 {
INY ;12
DEX ;13
}
}
DEC X_COUNTER
BMI .next_row
INX
TXA ; Outside the drawing part we need to put X back (divide by 2)
LSR
TAX
BNE .next_col
JMP .next_col
; Increment row
.next_row
DEC Y_COUNTER
BPL .notDone
RTS
.notDone
INC Y_LOC
INC SECTION_Y_START
JMP .rowLoop
; Draw player
tblHGRl