mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
float assign improvements
This commit is contained in:
parent
468c080859
commit
b4d82ba8e6
@ -753,6 +753,7 @@ class CodeGenerator:
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserving_registers(self, registers: Set[str]):
|
||||
# @todo option to avoid the sta $03/lda$03 when a is loaded anyway
|
||||
# this clobbers a ZP scratch register and is therefore safe to use in interrupts
|
||||
# see http://6502.org/tutorials/register_preservation.html
|
||||
if registers == {'A'}:
|
||||
@ -827,33 +828,61 @@ class CodeGenerator:
|
||||
raise CodeError("can only assign a byte or word to a register pair")
|
||||
|
||||
def generate_assign_mem_to_mem(self, lv: ParseResult.MemMappedValue, rvalue: ParseResult.MemMappedValue) -> None:
|
||||
r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address)
|
||||
r_str = rvalue.name or Parser.to_hex(rvalue.address)
|
||||
l_str = lv.name or Parser.to_hex(lv.address)
|
||||
if lv.datatype == DataType.BYTE:
|
||||
if rvalue.datatype != DataType.BYTE:
|
||||
raise CodeError("can only assign a byte to a byte", str(rvalue))
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tlda " + r_str)
|
||||
self.p("\t\tsta " + (lv.name or Parser.to_hex(lv.address)))
|
||||
self.p("\t\tsta " + l_str)
|
||||
elif lv.datatype == DataType.WORD:
|
||||
if rvalue.datatype == DataType.BYTE:
|
||||
with self.preserving_registers({'A'}):
|
||||
l_str = lv.name or Parser.to_hex(lv.address)
|
||||
self.p("\t\tlda " + r_str)
|
||||
self.p("\t\tsta " + l_str)
|
||||
self.p("\t\tlda #0")
|
||||
self.p("\t\tsta {:s}+1".format(l_str))
|
||||
elif rvalue.datatype == DataType.WORD:
|
||||
with self.preserving_registers({'A'}):
|
||||
l_str = lv.name or Parser.to_hex(lv.address)
|
||||
self.p("\t\tlda {:s}".format(r_str))
|
||||
self.p("\t\tsta {:s}".format(l_str))
|
||||
self.p("\t\tlda {:s}+1".format(r_str))
|
||||
self.p("\t\tsta {:s}+1".format(l_str))
|
||||
else:
|
||||
raise CodeError("can only assign a byte or word to a word", str(rvalue))
|
||||
elif lv.datatype == DataType.FLOAT:
|
||||
if rvalue.datatype == DataType.FLOAT:
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tlda " + r_str)
|
||||
self.p("\t\tsta " + l_str)
|
||||
self.p("\t\tlda {:s}+1".format(r_str))
|
||||
self.p("\t\tsta {:s}+1".format(l_str))
|
||||
self.p("\t\tlda {:s}+2".format(r_str))
|
||||
self.p("\t\tsta {:s}+2".format(l_str))
|
||||
self.p("\t\tlda {:s}+3".format(r_str))
|
||||
self.p("\t\tsta {:s}+3".format(l_str))
|
||||
self.p("\t\tlda {:s}+4".format(r_str))
|
||||
self.p("\t\tsta {:s}+4".format(l_str))
|
||||
elif rvalue.datatype == DataType.BYTE:
|
||||
with self.preserving_registers({'A', 'X', 'Y'}):
|
||||
self.p("\t\tldy " + r_str)
|
||||
self.p("\t\tjsr c64.FREADUY") # ubyte Y -> fac1
|
||||
self.p("\t\tldx #<" + l_str)
|
||||
self.p("\t\tldy #>" + l_str)
|
||||
self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY
|
||||
elif rvalue.datatype == DataType.WORD:
|
||||
with self.preserving_registers({'A', 'X', 'Y'}):
|
||||
self.p("\t\tlda " + r_str)
|
||||
self.p("\t\tldy {:s}+1".format(r_str))
|
||||
self.p("\t\tjsr c64util.GIVUAYF") # uword AY -> fac1
|
||||
self.p("\t\tldx #<" + l_str)
|
||||
self.p("\t\tldy #>" + l_str)
|
||||
self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY
|
||||
else:
|
||||
raise CodeError("unsupported rvalue to memfloat", str(rvalue))
|
||||
else:
|
||||
raise CodeError("can only assign memory to a memory mapped value for now "
|
||||
"(if you need other types, can't you use a var?)", str(rvalue))
|
||||
raise CodeError("invalid lvalue memmapped datatype", str(lv))
|
||||
|
||||
def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None:
|
||||
# Memory = Character
|
||||
|
139
reference.txt
139
reference.txt
@ -11,7 +11,7 @@ It uses the 64tass macro cross assembler to assemble it into binary files.
|
||||
|
||||
|
||||
|
||||
Memory Model
|
||||
MEMORY MODEL
|
||||
------------
|
||||
|
||||
Zero page: $00 - $ff
|
||||
@ -56,8 +56,8 @@ Free zero page addresses on the C-64:
|
||||
|
||||
|
||||
|
||||
IL program parsing structure:
|
||||
-----------------------------
|
||||
PROGRAM STRUCTURE
|
||||
-----------------
|
||||
|
||||
|
||||
OUTPUT MODES:
|
||||
@ -132,14 +132,16 @@ asmbinary "filename.bin" [, <offset>[, <length>]]
|
||||
|
||||
|
||||
|
||||
MACROS
|
||||
------
|
||||
ASSIGNMENTS
|
||||
-----------
|
||||
Assignment statements assign a single value to one or more variables or memory locations.
|
||||
If you know that you have to assign the same value to more than one thing at once, it is more
|
||||
efficient to write it as a multi-assign instead of several separate assignments. The compiler
|
||||
tries to detect this situation however and optimize it itself if it finds the case.
|
||||
|
||||
@todo macros are meta-code (written in Python syntax) that actually runs in a preprecessing step
|
||||
during the compilation, and produces output value that is then replaced on that point in the input source.
|
||||
Allows us to create pre calculated sine tables and such. Something like:
|
||||
target = value-expression
|
||||
target1 = target2 = target3 [,...] = value-expression
|
||||
|
||||
var .array sinetable ``[sin(x) * 10 for x in range(100)]``
|
||||
|
||||
|
||||
|
||||
@ -164,7 +166,60 @@ unless it is part of a subroutine call statement. For an indirect goto call, the
|
||||
Everything after a semicolon ';' is a comment and is ignored, however the comment (if it is the only thing
|
||||
on the line) is copied into the resulting assembly source code.
|
||||
|
||||
|
||||
|
||||
|
||||
SUBROUTINES DEFINITIONS
|
||||
-----------------------
|
||||
|
||||
Subroutines are parts of the code that can be repeatedly invoked using a subroutine call from elsewhere.
|
||||
Their definition, using the sub statement, includes the specification of the required input- and output parameters.
|
||||
For now, only register based parameters are supported (A, X, Y and paired registers, and the carry status bit SC as a special).
|
||||
The syntax is:
|
||||
|
||||
sub <identifier> ([proc_parameters]) -> ([proc_results]) {
|
||||
... statements ...
|
||||
}
|
||||
|
||||
proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters
|
||||
proc_results = comma separated list of <register> names specifying in which register(s) the output is returned.
|
||||
If the register name ends with a '?', that means the register doesn't contain a real return value but
|
||||
is clobbered in the process so the original value it had before calling the sub is no longer valid.
|
||||
This is not immediately useful for your own code, but the compiler needs this information to
|
||||
emit the correct assembly code to preserve the cpu registers if needed when the call is made.
|
||||
|
||||
|
||||
Subroutines that are pre-defined on a specific memory location (usually routines from ROM),
|
||||
can also be defined using the 'sub' statement. But in this case you don't supply a block with statements,
|
||||
but instead assign a memory address to it:
|
||||
|
||||
sub <identifier> ([proc_parameters]) -> ([proc_results]) = <address>
|
||||
|
||||
example: "sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3"
|
||||
|
||||
|
||||
SUBROUTINE CALLS
|
||||
----------------
|
||||
|
||||
You call a subroutine like this:
|
||||
subroutinename_or_address [!] ( [arguments...] )
|
||||
|
||||
Normally, the registers are preserved when calling the subroutine and restored on return.
|
||||
If you add a '!' after the name, no register preserving is done and the call essentially
|
||||
is just a single JSR instruction.
|
||||
Arguments should match the subroutine definition. You are allowed to omit the parameter names.
|
||||
If no definition is available (because you're directly calling memory or a label or something else),
|
||||
you can freely add arguments (but in this case they all have to be named).
|
||||
|
||||
To jump to a subroutine (without returning), prefix the subroutine call with the word 'goto'.
|
||||
Unlike gotos in other languages, here it take arguments as well, because it
|
||||
essentially is the same as calling a subroutine and only doing something different when it's finished.
|
||||
|
||||
|
||||
@todo support call non-register args (variable parameter passing)
|
||||
@todo support assigning call return values (so that you can assign these to other variables, and allows the subroutine call be an actual expression)
|
||||
|
||||
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
@ -302,7 +357,18 @@ il65_for_999_end ; code continues after this
|
||||
|
||||
|
||||
|
||||
MEMORY BLOCK OPERATIONS:
|
||||
MACROS
|
||||
------
|
||||
|
||||
@todo macros are meta-code (written in Python syntax) that actually runs in a preprecessing step
|
||||
during the compilation, and produces output value that is then replaced on that point in the input source.
|
||||
Allows us to create pre calculated sine tables and such. Something like:
|
||||
|
||||
var .array sinetable ``[sin(x) * 10 for x in range(100)]``
|
||||
|
||||
|
||||
MEMORY BLOCK OPERATIONS
|
||||
-----------------------
|
||||
|
||||
@todo matrix,list,string memory block operations:
|
||||
- matrix type operations (whole matrix, per row, per column, individual row/column)
|
||||
@ -323,57 +389,6 @@ these should call (or emit inline) optimized pieces of assembly code, so they ru
|
||||
|
||||
|
||||
|
||||
SUBROUTINES DEFINITIONS
|
||||
-----------------------
|
||||
|
||||
Subroutines are parts of the code that can be repeatedly invoked using a subroutine call from elsewhere.
|
||||
Their definition, using the sub statement, includes the specification of the required input- and output parameters.
|
||||
For now, only register based parameters are supported (A, X, Y and paired registers, and the carry status bit SC as a special).
|
||||
The syntax is:
|
||||
|
||||
sub <identifier> ([proc_parameters]) -> ([proc_results]) {
|
||||
... statements ...
|
||||
}
|
||||
|
||||
proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters
|
||||
proc_results = comma separated list of <register> names specifying in which register(s) the output is returned.
|
||||
If the register name ends with a '?', that means the register doesn't contain a real return value but
|
||||
is clobbered in the process so the original value it had before calling the sub is no longer valid.
|
||||
This is not immediately useful for your own code, but the compiler needs this information to
|
||||
emit the correct assembly code to preserve the cpu registers if needed when the call is made.
|
||||
|
||||
|
||||
Subroutines that are pre-defined on a specific memory location (usually routines from ROM),
|
||||
can also be defined using the 'sub' statement. But in this case you don't supply a block with statements,
|
||||
but instead assign a memory address to it:
|
||||
|
||||
sub <identifier> ([proc_parameters]) -> ([proc_results]) = <address>
|
||||
|
||||
example: "sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3"
|
||||
|
||||
|
||||
SUBROUTINE CALLS
|
||||
----------------
|
||||
|
||||
You call a subroutine like this:
|
||||
subroutinename_or_address [!] ( [arguments...] )
|
||||
|
||||
Normally, the registers are preserved when calling the subroutine and restored on return.
|
||||
If you add a '!' after the name, no register preserving is done and the call essentially
|
||||
is just a single JSR instruction.
|
||||
Arguments should match the subroutine definition. You are allowed to omit the parameter names.
|
||||
If no definition is available (because you're directly calling memory or a label or something else),
|
||||
you can freely add arguments (but in this case they all have to be named).
|
||||
|
||||
To jump to a subroutine (without returning), prefix the subroutine call with the word 'goto'.
|
||||
Unlike gotos in other languages, here it take arguments as well, because it
|
||||
essentially is the same as calling a subroutine and only doing something different when it's finished.
|
||||
|
||||
|
||||
@todo support call non-register args (variable parameter passing)
|
||||
@todo support assigning call return values (so that you can assign these to other variables, and allows the subroutine call be an actual expression)
|
||||
|
||||
|
||||
REGISTER PRESERVATION BLOCK: @todo (no)preserve
|
||||
----------------------------
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
output raw
|
||||
clobberzp
|
||||
|
||||
|
||||
~ ZP {
|
||||
; ZeroPage block definition:
|
||||
; base address is set to $04 (because $00 and $01 are used by the hardware, and $02/$03 are scratch)
|
||||
@ -271,17 +272,17 @@ start
|
||||
memfloat = cbyte3
|
||||
memfloat = cword2
|
||||
|
||||
; @todo float assignments that require ROM functions or shims:
|
||||
; float assignments that require ROM functions from c64lib:
|
||||
memfloat = Y
|
||||
memfloat = XY
|
||||
uninitfloat = Y
|
||||
uninitfloat = XY
|
||||
initfloat2 = Y
|
||||
initfloat2 = XY
|
||||
;initfloat2 = initbyte2 ; @todo support
|
||||
;initfloat2 = initword2 ; @todo support
|
||||
;initfloat1 = uninitfloat ; @todo support
|
||||
;initfloat1 = initfloat2 ; @todo support
|
||||
initfloat2 = initbyte2
|
||||
initfloat2 = initword2
|
||||
initfloat1 = uninitfloat
|
||||
initfloat1 = initfloat2
|
||||
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user