inline asm and fixed c64 libs

This commit is contained in:
Irmen de Jong 2018-10-16 02:26:35 +02:00
parent 263b197fec
commit 529c525081
10 changed files with 121 additions and 215 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
/.idea/
**/.idea/
/build/
/dist/
/output/

View File

@ -1,54 +1,26 @@
%option enable_floats
%import c64utils
~ main $c800 {
~ main {
sub start() {
memory ubyte mub = $c400
memory ubyte mub2 = $c401
memory uword muw = $c500
memory uword muw2 = $c502
memory byte mb = $c000
memory word mw = $c002
byte b = -100
ubyte ub = $c4
ubyte ub2 = $c4
uword uw = $c500
uword uw2 = $c502
word ww = -30000
float f1 = 1.23456
float f2 = -999.999e22
float f3 = -999.999e22
float f4 = -999.999e22
str s1 = "hallo"
str_p s2 = "hallo p"
str_s s3 = "hallo s"
str_ps s4 = "hallo ps"
byte[4] array1 = -99
ubyte[4] array2 = 244
word[4] array3 = -999
uword[4] array4 = 44444
float[4] array5 = [11.11, 22.22, 33.33, 44.44]
byte[3,7] matrix1 = [1,-2,3,-4,5,-6,1,-2,3,-4,5,-6,1,-2,3,-4,5,-6,22,33,44]
ubyte[3,7] matrix2 = [11,22,33,44,55,66, 11,22,33,44,55,66, 11,22,33,44,55,66, 55,66,77]
const ubyte screen_color = 9
const ubyte border_color = 2
const ubyte cursor_color = 7
memory ubyte screen = $d021
memory ubyte border = $d020
memory ubyte cursor = 646
screen = screen_color
border = border_color
cursor = cursor_color
return
sub nested () {
byte b
uword uw
}
}
sub second() {
byte b
uword uw
}
}
~ block2 $c000 {
return
}

View File

@ -486,7 +486,7 @@ class AstChecker(private val namespace: INameScope,
if(directive.parent !is Module) err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name==null)
err("invalid import directive, expected module name argument")
if(directive.args[0].name == (directive.parent as Module).name)
if(directive.args[0].name == (directive.parent as? Module)?.name)
err("invalid import directive, cannot import itself")
}
"%breakpoint" -> {

View File

@ -199,9 +199,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.line(subroutine.position)
// note: the caller has already written the arguments into the subroutine's parameter variables.
translate(subroutine.statements)
} else {
throw CompilerException("kernel subroutines (with memory address and no body) are not supported by StackVM: $subroutine")
}
} else if(subroutine.isNotEmpty())
throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine")
return super.process(subroutine)
}
@ -237,7 +236,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
is RepeatLoop -> translate(stmt)
is AnonymousScope -> translate(stmt)
is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these.
is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM")
is InlineAssembly -> translate(stmt)
else -> TODO("translate statement $stmt to stackvm")
}
}
@ -390,6 +389,10 @@ private class StatementTranslator(private val prog: IntermediateProgram,
}
}
private fun translate(stmt: InlineAssembly) {
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel = stmt.assembly)
}
private fun translate(stmt: Continue) {
prog.line(stmt.position)
if(continueStmtLabelStack.empty())

View File

@ -226,7 +226,8 @@ enum class Opcode {
NOP, // do nothing
BREAKPOINT, // breakpoint
TERMINATE, // end the program
LINE // track source file line number
LINE, // track source file line number
INLINE_ASSEMBLY // container to hold inline raw assembly code
}
val opcodesWithVarArgument = setOf(

View File

@ -49,7 +49,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
}
private fun out(str: String) = output.println(str)
private fun out(str: String?) = output.println(str)
fun compileToAssembly(): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ")
@ -58,7 +58,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
output = File("${program.name}.asm").printWriter()
output.use {
header()
for(block in program.blocks)
for(block in program.blocks) // todo sort by address (if specified) (blocks w/o address go LAST)
block2asm(block)
}
@ -108,10 +108,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
out("\t.null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
out("+\t.word 0")
out("_prog8_entrypoint\t; assembly code starts here\n")
out("\tjsr c64utils.init_system")
}
options.output == OutputType.PRG -> {
out("; ---- program without sys call ----")
out("* = ${program.loadAddress.toHex()}\n")
out("\tjsr c64utils.init_system")
}
options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----")
@ -119,10 +121,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
}
out("\tlda #0")
out("\ttay")
out("\ttax")
out("\tdex\t; init estack pointer to \$ff")
out("\tldx #\$ff\t; init estack pointer")
out("\tclc")
out("\tjmp main.start\t; jump to program entrypoint")
out("")
@ -621,6 +620,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
pushFloat(ins.arg!!.integerValue().toHex())
}
Opcode.INLINE_ASSEMBLY -> out(ins.callLabel) // All of the inline assembly is stored in the calllabel property.
else-> TODO("asm for $ins")
// Opcode.POP_MEM_B -> TODO()
// Opcode.POP_MEM_UB -> TODO()

View File

@ -84,6 +84,8 @@ fun executeImportDirective(import: Directive, importedFrom: Path): Module? {
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
throw SyntaxError("invalid import directive", import.position)
val moduleName = import.args[0].name!!
if("$moduleName.p8" == import.position.file)
throw SyntaxError("cannot import self", import.position)
if(importedModules.containsKey(moduleName))
return null

View File

@ -1363,6 +1363,7 @@ class StackVm(private var traceOutputFile: String?) {
P_carry = evalstack.pop().asBooleanValue
P_irqd = evalstack.pop().asBooleanValue
}
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
}

View File

@ -20,11 +20,11 @@
memory byte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
memory byte COLOR = $0286 ; cursor color
memory byte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
memory byte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
memory word CINV = $0314 ; IRQ vector
memory word NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
memory word RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
memory word IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
memory word NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
memory word RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
memory word IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
memory byte[40, 25] Screen = $0400 ; default character screen matrix
memory byte[40, 25] Colors = $d800 ; character screen colors
@ -110,22 +110,22 @@
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
; checked functions below:
asmsub MOVFM (mflpt: word @ AY) -> clobbers(A,Y) -> () = $bba2 ; load mflpt value from memory in A/Y into fac1
asmsub MOVFM (mflpt: uword @ AY) -> clobbers(A,Y) -> () = $bba2 ; load mflpt value from memory in A/Y into fac1
asmsub FREADMEM () -> clobbers(A,Y) -> () = $bba6 ; load mflpt value from memory in $22/$23 into fac1
asmsub CONUPK (mflpt: word @ AY) -> clobbers(A,Y) -> () = $ba8c ; load mflpt value from memory in A/Y into fac2
asmsub CONUPK (mflpt: uword @ AY) -> clobbers(A,Y) -> () = $ba8c ; load mflpt value from memory in A/Y into fac2
asmsub FAREADMEM () -> clobbers(A,Y) -> () = $ba90 ; load mflpt value from memory in $22/$23 into fac2
asmsub MOVFA () -> clobbers(A,X) -> () = $bbfc ; copy fac2 to fac1
asmsub MOVAF () -> clobbers(A,X) -> () = $bc0c ; copy fac1 to fac2 (rounded)
asmsub MOVEF () -> clobbers(A,X) -> () = $bc0f ; copy fac1 to fac2
asmsub FTOMEMXY (mflpt: word @ XY) -> clobbers(A,Y) -> () = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
asmsub FTOMEMXY (mflpt: uword @ XY) -> clobbers(A,Y) -> () = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
; (use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal order)
asmsub FTOSWORDYA () -> clobbers(X) -> (byte @ Y, byte @ A) = $b1aa
asmsub FTOSWORDYA () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b1aa
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
; (use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal order)
asmsub GETADR () -> clobbers(X) -> (byte @ Y, byte @ A) = $b7f7
asmsub GETADR () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b7f7
asmsub QINT () -> clobbers(A,X,Y) -> () = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
asmsub AYINT () -> clobbers(A,X,Y) -> () = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
@ -136,41 +136,41 @@ asmsub AYINT () -> clobbers(A,X,Y) -> () = $b1bf ; fac1-> signed word in 100
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
asmsub GIVAYF (lo: byte @ Y, hi: byte @ A) -> clobbers(A,X,Y) -> () = $b391
asmsub GIVAYF (lo: ubyte @ Y, hi: ubyte @ A) -> clobbers(A,X,Y) -> () = $b391
asmsub FREADUY (ubyte: byte @ Y) -> clobbers(A,X,Y) -> () = $b3a2 ; 8 bit unsigned Y -> float in fac1
asmsub FREADSA (sbyte: byte @ A) -> clobbers(A,X,Y) -> () = $bc3c ; 8 bit signed A -> float in fac1
asmsub FREADSTR (length: byte @ A) -> clobbers(A,X,Y) -> () = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
asmsub FREADUY (unsigned: ubyte @ Y) -> clobbers(A,X,Y) -> () = $b3a2 ; 8 bit unsigned Y -> float in fac1
asmsub FREADSA (signed: ubyte @ A) -> clobbers(A,X,Y) -> () = $bc3c ; 8 bit signed A -> float in fac1
asmsub FREADSTR (length: ubyte @ A) -> clobbers(A,X,Y) -> () = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
asmsub FPRINTLN () -> clobbers(A,X,Y) -> () = $aabc ; print string of fac1, on one line (= with newline)
asmsub FOUT () -> clobbers(X) -> (word @ AY) = $bddd ; fac1 -> string, address returned in AY ($0100)
asmsub FOUT () -> clobbers(X) -> (uword @ AY) = $bddd ; fac1 -> string, address returned in AY ($0100)
asmsub FADDH () -> clobbers(A,X,Y) -> () = $b849 ; fac1 += 0.5, for rounding- call this before INT
asmsub MUL10 () -> clobbers(A,X,Y) -> () = $bae2 ; fac1 *= 10
asmsub DIV10 () -> clobbers(A,X,Y) -> () = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
asmsub FCOMP (mflpt: word @ AY) -> clobbers(X,Y) -> (byte @ A) = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
asmsub FCOMP (mflpt: uword @ AY) -> clobbers(X,Y) -> (ubyte @ A) = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
asmsub FADDT () -> clobbers(A,X,Y) -> () = $b86a ; fac1 += fac2
asmsub FADD (mflpt: word @ AY) -> clobbers(A,X,Y) -> () = $b867 ; fac1 += mflpt value from A/Y
asmsub FADD (mflpt: uword @ AY) -> clobbers(A,X,Y) -> () = $b867 ; fac1 += mflpt value from A/Y
asmsub FSUBT () -> clobbers(A,X,Y) -> () = $b853 ; fac1 = fac2-fac1 mind the order of the operands
asmsub FSUB (mflpt: word @ AY) -> clobbers(A,X,Y) -> () = $b850 ; fac1 = mflpt from A/Y - fac1
asmsub FSUB (mflpt: uword @ AY) -> clobbers(A,X,Y) -> () = $b850 ; fac1 = mflpt from A/Y - fac1
asmsub FMULTT () -> clobbers(A,X,Y) -> () = $ba2b ; fac1 *= fac2
asmsub FMULT (mflpt: word @ AY) -> clobbers(A,X,Y) -> () = $ba28 ; fac1 *= mflpt value from A/Y
asmsub FMULT (mflpt: uword @ AY) -> clobbers(A,X,Y) -> () = $ba28 ; fac1 *= mflpt value from A/Y
asmsub FDIVT () -> clobbers(A,X,Y) -> () = $bb12 ; fac1 = fac2/fac1 mind the order of the operands
asmsub FDIV (mflpt: word @ AY) -> clobbers(A,X,Y) -> () = $bb0f ; fac1 = mflpt in A/Y / fac1
asmsub FDIV (mflpt: uword @ AY) -> clobbers(A,X,Y) -> () = $bb0f ; fac1 = mflpt in A/Y / fac1
asmsub FPWRT () -> clobbers(A,X,Y) -> () = $bf7b ; fac1 = fac2 ** fac1
asmsub FPWR (mflpt: word @ AY) -> clobbers(A,X,Y) -> () = $bf78 ; fac1 = fac2 ** mflpt from A/Y
asmsub FPWR (mflpt: uword @ AY) -> clobbers(A,X,Y) -> () = $bf78 ; fac1 = fac2 ** mflpt from A/Y
asmsub NOTOP () -> clobbers(A,X,Y) -> () = $aed4 ; fac1 = NOT(fac1)
asmsub INT () -> clobbers(A,X,Y) -> () = $bccc ; INT() truncates, use FADDH first to round instead of trunc
asmsub LOG () -> clobbers(A,X,Y) -> () = $b9ea ; fac1 = LN(fac1) (natural log)
asmsub SGN () -> clobbers(A,X,Y) -> () = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
asmsub SIGN () -> clobbers() -> (byte @ A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
asmsub SIGN () -> clobbers() -> (ubyte @ A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
asmsub ABS () -> clobbers() -> () = $bc58 ; fac1 = ABS(fac1)
asmsub SQR () -> clobbers(A,X,Y) -> () = $bf71 ; fac1 = SQRT(fac1)
asmsub EXP () -> clobbers(A,X,Y) -> () = $bfed ; fac1 = EXP(fac1) (e ** fac1)
asmsub NEGOP () -> clobbers(A) -> () = $bfb4 ; switch the sign of fac1
asmsub RND () -> clobbers(A,X,Y) -> () = $e097 ; fac1 = RND() (use RNDA instead)
asmsub RNDA (acc: byte @ A) -> clobbers(A,X,Y) -> () = $e09a ; fac1 = RND(A)
asmsub RNDA (acc: ubyte @ A) -> clobbers(A,X,Y) -> () = $e09a ; fac1 = RND(A)
asmsub COS () -> clobbers(A,X,Y) -> () = $e264 ; fac1 = COS(fac1)
asmsub SIN () -> clobbers(A,X,Y) -> () = $e26b ; fac1 = SIN(fac1)
asmsub TAN () -> clobbers(A,X,Y) -> () = $e2b4 ; fac1 = TAN(fac1)
@ -195,41 +195,41 @@ asmsub CINT () -> clobbers(A,X,Y) -> () = $FF81 ; (alias: SCINIT) initial
asmsub IOINIT () -> clobbers(A, X) -> () = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
asmsub RAMTAS () -> clobbers(A,X,Y) -> () = $FF87 ; initialize RAM, tape buffer, screen
asmsub RESTOR () -> clobbers(A,X,Y) -> () = $FF8A ; restore default I/O vectors
asmsub VECTOR (dir: byte @ Pc, userptr: word @ XY) -> clobbers(A,Y) -> () = $FF8D ; read/set I/O vector table
asmsub SETMSG (value: byte @ A) -> clobbers() -> () = $FF90 ; set Kernal message control flag
asmsub SECOND (address: byte @ A) -> clobbers(A) -> () = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
asmsub TKSA (address: byte @ A) -> clobbers(A) -> () = $FF96 ; (alias: TALKSA) send secondary address after TALK
asmsub MEMTOP (dir: byte @ Pc, address: word @ XY) -> clobbers() -> (word @ XY) = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (dir: byte @ Pc, address: word @ XY) -> clobbers() -> (word @ XY) = $FF9C ; read/set bottom of memory pointer
asmsub VECTOR (dir: ubyte @ Pc, userptr: uword @ XY) -> clobbers(A,Y) -> () = $FF8D ; read/set I/O vector table
asmsub SETMSG (value: ubyte @ A) -> clobbers() -> () = $FF90 ; set Kernal message control flag
asmsub SECOND (address: ubyte @ A) -> clobbers(A) -> () = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
asmsub TKSA (address: ubyte @ A) -> clobbers(A) -> () = $FF96 ; (alias: TALKSA) send secondary address after TALK
asmsub MEMTOP (dir: ubyte @ Pc, address: uword @ XY) -> clobbers() -> (uword @ XY) = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (dir: ubyte @ Pc, address: uword @ XY) -> clobbers() -> (uword @ XY) = $FF9C ; read/set bottom of memory pointer
asmsub SCNKEY () -> clobbers(A,X,Y) -> () = $FF9F ; scan the keyboard
asmsub SETTMO (timeout: byte @ A) -> clobbers() -> () = $FFA2 ; set time-out flag for IEEE bus
asmsub ACPTR () -> clobbers() -> (byte @ A) = $FFA5 ; (alias: IECIN) input byte from serial bus
asmsub CIOUT (databyte: byte @ A) -> clobbers() -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
asmsub SETTMO (timeout: ubyte @ A) -> clobbers() -> () = $FFA2 ; set time-out flag for IEEE bus
asmsub ACPTR () -> clobbers() -> (ubyte @ A) = $FFA5 ; (alias: IECIN) input byte from serial bus
asmsub CIOUT (databyte: ubyte @ A) -> clobbers() -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
asmsub UNTLK () -> clobbers(A) -> () = $FFAB ; command serial bus device to UNTALK
asmsub UNLSN () -> clobbers(A) -> () = $FFAE ; command serial bus device to UNLISTEN
asmsub LISTEN (device: byte @ A) -> clobbers(A) -> () = $FFB1 ; command serial bus device to LISTEN
asmsub TALK (device: byte @ A) -> clobbers(A) -> () = $FFB4 ; command serial bus device to TALK
asmsub READST () -> clobbers() -> (byte @ A) = $FFB7 ; read I/O status word
asmsub SETLFS (logical: byte @ A, device: byte @ X, address: byte @ Y) -> clobbers() -> () = $FFBA ; set logical file parameters
asmsub SETNAM (namelen: byte @ A, filename: word @ XY) -> clobbers() -> () = $FFBD ; set filename parameters
asmsub LISTEN (device: ubyte @ A) -> clobbers(A) -> () = $FFB1 ; command serial bus device to LISTEN
asmsub TALK (device: ubyte @ A) -> clobbers(A) -> () = $FFB4 ; command serial bus device to TALK
asmsub READST () -> clobbers() -> (ubyte @ A) = $FFB7 ; read I/O status word
asmsub SETLFS (logical: ubyte @ A, device: ubyte @ X, address: ubyte @ Y) -> clobbers() -> () = $FFBA ; set logical file parameters
asmsub SETNAM (namelen: ubyte @ A, filename: uword @ XY) -> clobbers() -> () = $FFBD ; set filename parameters
asmsub OPEN () -> clobbers(A,X,Y) -> () = $FFC0 ; (via 794 ($31A)) open a logical file
asmsub CLOSE (logical: byte @ A) -> clobbers(A,X,Y) -> () = $FFC3 ; (via 796 ($31C)) close a logical file
asmsub CHKIN (logical: byte @ X) -> clobbers(A,X) -> () = $FFC6 ; (via 798 ($31E)) define an input channel
asmsub CHKOUT (logical: byte @ X) -> clobbers(A,X) -> () = $FFC9 ; (via 800 ($320)) define an output channel
asmsub CLOSE (logical: ubyte @ A) -> clobbers(A,X,Y) -> () = $FFC3 ; (via 796 ($31C)) close a logical file
asmsub CHKIN (logical: ubyte @ X) -> clobbers(A,X) -> () = $FFC6 ; (via 798 ($31E)) define an input channel
asmsub CHKOUT (logical: ubyte @ X) -> clobbers(A,X) -> () = $FFC9 ; (via 800 ($320)) define an output channel
asmsub CLRCHN () -> clobbers(A,X) -> () = $FFCC ; (via 802 ($322)) restore default devices
asmsub CHRIN () -> clobbers(Y) -> (byte @ A) = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
asmsub CHROUT (char: byte @ A) -> clobbers() -> () = $FFD2 ; (via 806 ($326)) output a character
asmsub LOAD (verify: byte @ A, address: word @ XY) -> clobbers() -> (byte @Pc, byte @ A, byte @ X, byte @ Y) = $FFD5 ; (via 816 ($330)) load from device
asmsub SAVE (zp_startaddr: byte @ A, endaddr: word @ XY) -> clobbers() -> (byte @ Pc, byte @ A) = $FFD8 ; (via 818 ($332)) save to a device
asmsub SETTIM (low: byte @ A, middle: byte @ X, high: byte @ Y) -> clobbers() -> () = $FFDB ; set the software clock
asmsub RDTIM () -> clobbers() -> (byte @ A, byte @ X, byte @ Y) = $FFDE ; read the software clock
asmsub STOP () -> clobbers(A,X) -> (byte @ Pz, byte @ Pc) = $FFE1 ; (via 808 ($328)) check the STOP key
asmsub GETIN () -> clobbers(X,Y) -> (byte @ A) = $FFE4 ; (via 810 ($32A)) get a character
asmsub CHRIN () -> clobbers(Y) -> (ubyte @ A) = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
asmsub CHROUT (char: ubyte @ A) -> clobbers() -> () = $FFD2 ; (via 806 ($326)) output a character
asmsub LOAD (verify: ubyte @ A, address: uword @ XY) -> clobbers() -> (ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y) = $FFD5 ; (via 816 ($330)) load from device
asmsub SAVE (zp_startaddr: ubyte @ A, endaddr: uword @ XY) -> clobbers() -> (ubyte @ Pc, ubyte @ A) = $FFD8 ; (via 818 ($332)) save to a device
asmsub SETTIM (low: ubyte @ A, middle: ubyte @ X, high: ubyte @ Y) -> clobbers() -> () = $FFDB ; set the software clock
asmsub RDTIM () -> clobbers() -> (ubyte @ A, ubyte @ X, ubyte @ Y) = $FFDE ; read the software clock
asmsub STOP () -> clobbers(A,X) -> (ubyte @ Pz, ubyte @ Pc) = $FFE1 ; (via 808 ($328)) check the STOP key
asmsub GETIN () -> clobbers(X,Y) -> (ubyte @ A) = $FFE4 ; (via 810 ($32A)) get a character
asmsub CLALL () -> clobbers(A,X) -> () = $FFE7 ; (via 812 ($32C)) close all files
asmsub UDTIM () -> clobbers(A,X) -> () = $FFEA ; update the software clock
asmsub SCREEN () -> clobbers() -> (byte @ X, byte @ Y) = $FFED ; read number of screen rows and columns
asmsub PLOT (dir: byte @ Pc, col: byte @ Y, row: byte @ X) -> clobbers() -> (byte @ X, byte @ Y) = $FFF0 ; read/set position of cursor on screen
asmsub IOBASE () -> clobbers() -> (byte @ X, byte @ Y) = $FFF3 ; read base address of I/O devices
asmsub SCREEN () -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFED ; read number of screen rows and columns
asmsub PLOT (dir: ubyte @ Pc, col: ubyte @ Y, row: ubyte @ X) -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFF0 ; read/set position of cursor on screen
asmsub IOBASE () -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFF3 ; read base address of I/O devices
; ---- end of C64 kernal routines ----

View File

@ -43,7 +43,7 @@ asmsub init_system () -> clobbers(A,X,Y) -> () {
}}
}
asmsub byte2decimal (ubyte: byte @ A) -> clobbers() -> (byte @ Y, byte @ X, byte @ A) {
asmsub byte2decimal (value: ubyte @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X, ubyte @ A) {
; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
%asm {{
ldy #$2f
@ -60,7 +60,7 @@ asmsub byte2decimal (ubyte: byte @ A) -> clobbers() -> (byte @ Y, byte @ X, by
}}
}
asmsub byte2hex (ubyte: byte @ A) -> clobbers(A) -> (byte @ X, byte @ Y) {
asmsub byte2hex (value: ubyte @ A) -> clobbers(A) -> (ubyte @ X, ubyte @ Y) {
; ---- A to hex string in XY (first hex char in X, second hex char in Y)
%asm {{
pha
@ -77,13 +77,13 @@ asmsub byte2hex (ubyte: byte @ A) -> clobbers(A) -> (byte @ X, byte @ Y) {
tax
rts
hex_digits .str "0123456789abcdef" ; can probably be reused for other stuff as well
hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
}}
}
str word2hex_output = "1234" ; 0-terminated, to make printing easier
asmsub word2hex (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub word2hex (dataword: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- convert 16 bit word in X/Y into 4-character hexadecimal string into memory 'word2hex_output'
%asm {{
stx c64.SCRATCH_ZP2
@ -99,8 +99,8 @@ asmsub word2hex (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
}}
}
byte[3] word2bcd_bcdbuff = [0, 0, 0]
asmsub word2bcd (dataword: word @ XY) -> clobbers(A,X) -> () {
ubyte[3] word2bcd_bcdbuff = [0, 0, 0]
asmsub word2bcd (dataword: uword @ XY) -> clobbers(A,X) -> () {
; Convert an 16 bit binary value to BCD
;
; This function converts a 16 bit binary value in X/Y into a 24 bit BCD. It
@ -137,8 +137,8 @@ asmsub word2bcd (dataword: word @ XY) -> clobbers(A,X) -> () {
}
byte[5] word2decimal_output = 0
asmsub word2decimal (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
ubyte[5] word2decimal_output = 0
asmsub word2decimal (dataword: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output'
%asm {{
jsr word2bcd
@ -202,7 +202,7 @@ asmsub FREADUS32 () -> clobbers(A,X,Y) -> () {
}}
}
asmsub FREADS24AXY (lo: byte @ A, mid: byte @ X, hi: byte @ Y) -> clobbers(A,X,Y) -> () {
asmsub FREADS24AXY (lo: ubyte @ A, mid: ubyte @ X, hi: ubyte @ Y) -> clobbers(A,X,Y) -> () {
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
%asm {{
@ -219,7 +219,7 @@ asmsub FREADS24AXY (lo: byte @ A, mid: byte @ X, hi: byte @ Y) -> clobbers(A,X
}}
}
asmsub GIVUAYF (uword: word @ AY) -> clobbers(A,X,Y) -> () {
asmsub GIVUAYF (value: uword @ AY) -> clobbers(A,X,Y) -> () {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
%asm {{
sty $62
@ -230,7 +230,7 @@ asmsub GIVUAYF (uword: word @ AY) -> clobbers(A,X,Y) -> () {
}}
}
asmsub GIVAYFAY (sword: word @ AY) -> clobbers(A,X,Y) -> () {
asmsub GIVAYFAY (value: uword @ AY) -> clobbers(A,X,Y) -> () {
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
%asm {{
sta c64.SCRATCH_ZP1
@ -240,7 +240,7 @@ asmsub GIVAYFAY (sword: word @ AY) -> clobbers(A,X,Y) -> () {
}}
}
asmsub FTOSWRDAY () -> clobbers(X) -> (word @ AY) {
asmsub FTOSWRDAY () -> clobbers(X) -> (uword @ AY) {
; ---- fac1 to signed word in A/Y
%asm {{
jsr c64.FTOSWORDYA ; note the inverse Y/A order
@ -251,7 +251,7 @@ asmsub FTOSWRDAY () -> clobbers(X) -> (word @ AY) {
}}
}
asmsub GETADRAY () -> clobbers(X) -> (word @ AY) {
asmsub GETADRAY () -> clobbers(X) -> (uword @ AY) {
; ---- fac1 to unsigned word in A/Y
%asm {{
jsr c64.GETADR ; this uses the inverse order, Y/A
@ -263,7 +263,7 @@ asmsub GETADRAY () -> clobbers(X) -> (word @ AY) {
}
asmsub copy_mflt (source: word @ XY) -> clobbers(A,Y) -> () {
asmsub copy_mflt (source: uword @ XY) -> clobbers(A,Y) -> () {
; ---- copy a 5 byte MFLT floating point variable to another place
; input: X/Y = source address, c64.SCRATCH_ZPWORD1 = destination address
%asm {{
@ -289,7 +289,7 @@ asmsub copy_mflt (source: word @ XY) -> clobbers(A,Y) -> () {
}}
}
asmsub float_add_one (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub float_add_one (mflt: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- add 1 to the MFLT pointed to by X/Y. Clobbers A, X, Y
%asm {{
stx c64.SCRATCH_ZP1
@ -305,7 +305,7 @@ asmsub float_add_one (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
}}
}
asmsub float_sub_one (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub float_sub_one (mflt: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- subtract 1 from the MFLT pointed to by X/Y. Clobbers A, X, Y
%asm {{
stx c64.SCRATCH_ZP1
@ -322,7 +322,7 @@ asmsub float_sub_one (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
}}
}
asmsub float_add_SW1_to_XY (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub float_add_SW1_to_XY (mflt: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- add MFLT pointed to by SCRATCH_ZPWORD1 to the MFLT pointed to by X/Y. Clobbers A, X, Y
%asm {{
stx c64.SCRATCH_ZP1
@ -338,7 +338,7 @@ asmsub float_add_SW1_to_XY (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
}}
}
asmsub float_sub_SW1_from_XY (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub float_sub_SW1_from_XY (mflt: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- subtract MFLT pointed to by SCRATCH_ZPWORD1 from the MFLT pointed to by X/Y. Clobbers A, X, Y
%asm {{
stx c64.SCRATCH_ZP1
@ -363,7 +363,7 @@ asmsub float_sub_SW1_from_XY (mflt: word @ XY) -> clobbers(A,X,Y) -> () {
; ---- this block contains (character) Screen and text I/O related functions ----
asmsub clear_screen (char: byte @ A, color: byte @ Y) -> clobbers() -> () {
asmsub clear_screen (char: ubyte @ A, color: ubyte @ Y) -> clobbers() -> () {
; ---- clear the character screen with the given fill character and character color.
; (assumes screen is at $0400, could be altered in the future with self-modifying code)
; @todo some byte var to set the SCREEN ADDR HI BYTE
@ -393,7 +393,7 @@ _loop lda #0
}
asmsub scroll_left_full (alsocolors: byte @ Pc) -> clobbers(A, X, Y) -> () {
asmsub scroll_left_full (alsocolors: ubyte @ Pc) -> clobbers(A, X, Y) -> () {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
@ -452,7 +452,7 @@ _scroll_screen ; scroll the screen memory
}
asmsub scroll_right_full (alsocolors: byte @ Pc) -> clobbers(A,X) -> () {
asmsub scroll_right_full (alsocolors: ubyte @ Pc) -> clobbers(A,X) -> () {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
@ -503,7 +503,7 @@ _scroll_screen ; scroll the screen memory
}
asmsub scroll_up_full (alsocolors: byte @ Pc) -> clobbers(A,X) -> () {
asmsub scroll_up_full (alsocolors: ubyte @ Pc) -> clobbers(A,X) -> () {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
@ -554,7 +554,7 @@ _scroll_screen ; scroll the screen memory
}
asmsub scroll_down_full (alsocolors: byte @ Pc) -> clobbers(A,X) -> () {
asmsub scroll_down_full (alsocolors: ubyte @ Pc) -> clobbers(A,X) -> () {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
@ -606,7 +606,7 @@ _scroll_screen ; scroll the screen memory
asmsub print_string (address: word @ XY) -> clobbers(A,Y) -> () {
asmsub print_string (address: uword @ XY) -> clobbers(A,Y) -> () {
; ---- print null terminated string from X/Y
; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
@ -625,7 +625,7 @@ asmsub print_string (address: word @ XY) -> clobbers(A,Y) -> () {
}
asmsub print_pstring (address: word @ XY) -> clobbers(A,X) -> (byte @ Y) {
asmsub print_pstring (address: uword @ XY) -> clobbers(A,X) -> (ubyte @ Y) {
; ---- print pstring (length as first byte) from X/Y, returns str len in Y
%asm {{
stx c64.SCRATCH_ZP1
@ -644,36 +644,7 @@ asmsub print_pstring (address: word @ XY) -> clobbers(A,X) -> (byte @ Y) {
}
asmsub print_pimmediate () -> clobbers() -> () {
; ---- print pstring in memory immediately following the subroutine fast call instruction
; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
%asm {{
tsx
lda $102,x
tay ; put high byte in y
lda $101,x
tax ; and low byte in x.
inx
bne +
iny
+ jsr print_pstring ; print string in XY, returns string length in y.
tya
tsx
clc
adc $101,x ; add content of 1st (length) byte to return addr.
bcc + ; if that made the low byte roll over to 00,
inc $102,x ; then increment the high byte too.
+ clc
adc #1 ; now add 1 for the length byte itself.
sta $101,x
bne + ; if that made it (the low byte) roll over to 00,
inc $102,x ; increment the high byte of the return addr too.
+ rts
}}
}
asmsub print_byte_decimal0 (ubyte: byte @ A) -> clobbers(A,X,Y) -> () {
asmsub print_byte_decimal0 (value: ubyte @ A) -> clobbers(A,X,Y) -> () {
; ---- print the byte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
jsr byte2decimal
@ -688,7 +659,7 @@ asmsub print_byte_decimal0 (ubyte: byte @ A) -> clobbers(A,X,Y) -> () {
}
asmsub print_byte_decimal (ubyte: byte @ A) -> clobbers(A,X,Y) -> () {
asmsub print_byte_decimal (value: ubyte @ A) -> clobbers(A,X,Y) -> () {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
jsr byte2decimal
@ -709,7 +680,7 @@ _print_tens txa
}
asmsub print_byte_hex (prefix: byte @ Pc, ubyte: byte @ A) -> clobbers(A,X,Y) -> () {
asmsub print_byte_hex (prefix: ubyte @ Pc, value: ubyte @ A) -> clobbers(A,X,Y) -> () {
; ---- print the byte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
bcc +
@ -726,7 +697,7 @@ asmsub print_byte_hex (prefix: byte @ Pc, ubyte: byte @ A) -> clobbers(A,X,Y)
}
asmsub print_word_hex (prefix: byte @ Pc, dataword: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub print_word_hex (prefix: ubyte @ Pc, dataword: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- print the (unsigned) word in X/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@ -740,8 +711,9 @@ asmsub print_word_hex (prefix: byte @ Pc, dataword: word @ XY) -> clobbers(A,X,
}
asmsub print_word_decimal0 (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub print_word_decimal0 (dataword: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- print the (unsigned) word in X/Y in decimal form, with left padding 0s (5 positions total)
; @todo shorter in loop form?
%asm {{
jsr word2decimal
lda word2decimal_output
@ -758,7 +730,7 @@ asmsub print_word_decimal0 (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
}
asmsub print_word_decimal (dataword: word @ XY) -> clobbers(A,X,Y) -> () {
asmsub print_word_decimal (dataword: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- print the word in X/Y in decimal form, without left padding 0s
%asm {{
jsr word2decimal
@ -791,8 +763,8 @@ _pr_decimal
}
asmsub input_chars (buffer: word @ AX) -> clobbers(A) -> (byte @ Y) {
; ---- Input a string (max. 80 chars) from the keyboard.
asmsub input_chars (buffer: uword @ AX) -> clobbers(A) -> (ubyte @ Y) {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length.
; It assumes the keyboard is selected as I/O channel!!
%asm {{
@ -813,48 +785,3 @@ asmsub input_chars (buffer: word @ AX) -> clobbers(A) -> (byte @ Y) {
}
} ; ---- end block c64scr
;asmsub memcopy_basic () -> clobbers(A,X,Y) -> () {
; ; ---- copy a memory block by using a BASIC ROM routine
; ; it calls a function from the basic interpreter, so:
; ; - BASIC ROM must be banked in
; ; - the source block must be readable (so no RAM hidden under BASIC, Kernal, or I/O)
; ; - the target block must be writable (so no RAM hidden under I/O)
; ; higher addresses are copied first, so:
; ; - moving data to higher addresses works even if areas overlap
; ; - moving data to lower addresses only works if areas do not overlap
; %asm {{
; lda #<src_start
; ldx #>src_start
; sta $5f
; stx $60
; lda #<src_end
; ldx #>src_end
; sta $5a
; stx $5b
; lda #<(target_start + src_end - src_start)
; ldx #>(target_start + src_end - src_start)
; sta $58
; stx $59
; jmp $a3bf
; }
;}
; macro version of the above memcopy_basic routine:
; MACRO PARAMS src_start, src_end, target_start
; lda #<src_start
; ldx #>src_start
; sta $5f
; stx $60
; lda #<src_end
; ldx #>src_end
; sta $5a
; stx $5b
; lda #<(target_start + src_end - src_start)
; ldx #>(target_start + src_end - src_start)
; sta $58
; stx $59
; jsr $a3bf