Compare commits

..

28 Commits

Author SHA1 Message Date
9d4ec4a9b2 syntaxfile 2021-01-24 00:42:26 +01:00
cdc6d9aa65 moved cx16 imageviewer into its own git repo. Version 6.0. 2021-01-23 23:49:17 +01:00
997bc21feb added offsetof() to get the byte offset of struct members. 2021-01-23 23:11:57 +01:00
975af4764d remove no longer needed strlen() calls from diskio routines 2021-01-23 22:46:46 +01:00
bf69219f98 allow uwordpointer[index] syntax as equivalent to @(uwordpointer+index) index can be >255 here! 2021-01-23 22:39:30 +01:00
f34f9329f1 fixed bug in memcopy 2021-01-23 19:49:53 +01:00
90271d0dcd textelite was okay 2021-01-23 19:01:02 +01:00
195cd7597d fix pointer-to-pointer assignment 2021-01-23 18:50:46 +01:00
4a81406262 fix diskio rename() and delete() 2021-01-23 17:57:30 +01:00
f9fd426843 Merge branch 'pointer-index-optimize'
# Conflicts:
#	docs/source/todo.rst
2021-01-23 15:57:23 +01:00
e612056ecd more optimal screen pointer access in plasma.p8 example 2021-01-23 15:54:18 +01:00
6f0103398b fix Y register clobbering in pointer access code 2021-01-23 15:24:41 +01:00
afb60db382 todo 2021-01-20 18:43:08 +01:00
5731b876ff textelite save bug found 2021-01-20 01:36:46 +01:00
055f917a2e fixed missing code for certain memread expressions when casted to uword 2021-01-20 01:30:11 +01:00
4ed7fb771c started pointer access optimization 2021-01-20 00:17:33 +01:00
c328e9018c cx16 assembler was moved into its own github repo 2021-01-18 01:38:33 +01:00
b270f6f713 added cx16.rombank() and rambank(). Select kernal rom in i/o heavy programs for faster disk i/o 2021-01-17 19:16:21 +01:00
5c13918f11 cx16 reset_system() bank selection change 2021-01-17 18:28:43 +01:00
40cc216557 optimize pointer var access if var is already on zeropage 2021-01-16 18:31:37 +01:00
1481f92cb0 optimize memory read expression of ptr + constant index 2021-01-16 17:41:15 +01:00
76d54fbe5c optimize assignment to memory pointer with fixed byte offset 2021-01-15 20:46:47 +01:00
9f72779cdc optimize assignment from memory pointer with fixed byte offset 2021-01-15 20:09:13 +01:00
3dcef89a74 optimize (zp),y instructions for 65c02 to use (zp) 2021-01-15 19:14:35 +01:00
46373717b6 get rid of unused ci image format reader 2021-01-15 18:29:25 +01:00
7277c08fa6 added textio.spc(). assem tweaks. 2021-01-14 22:51:09 +01:00
04e75455c4 assem tweaks 2021-01-14 21:07:06 +01:00
8ac17ae14e fix assem parsing of 4 letter instructions 2021-01-14 18:41:29 +01:00
47 changed files with 580 additions and 3571 deletions

View File

@ -500,19 +500,23 @@ sys {
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
%asm {{
ldx cx16.r0
stx P8ZP_SCRATCH_W1
stx P8ZP_SCRATCH_W1 ; source in ZP
ldx cx16.r0+1
stx P8ZP_SCRATCH_W1+1
ldx cx16.r1
stx P8ZP_SCRATCH_W2
stx P8ZP_SCRATCH_W2 ; target in ZP
ldx cx16.r1+1
stx P8ZP_SCRATCH_W2+1
cpy #0
bne _longcopy
; copy <= 255
tay
_remainder
; copy <= 255 bytes
tay
bne _copyshort
rts ; nothing to copy
_copyshort
; decrease source and target pointers so we can simply index by Y
lda P8ZP_SCRATCH_W1
bne +
dec P8ZP_SCRATCH_W1+1
@ -521,18 +525,19 @@ _remainder
bne +
dec P8ZP_SCRATCH_W2+1
+ dec P8ZP_SCRATCH_W2
- lda (P8ZP_SCRATCH_W1), y
sta (P8ZP_SCRATCH_W2), y
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
dey
bne -
rts
_longcopy
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
tya
tax ; x = num pages (1+)
ldy #0
- lda (P8ZP_SCRATCH_W1),y ; copy a page at a time
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
iny
bne -
@ -541,7 +546,8 @@ _longcopy
dex
bne -
ldy P8ZP_SCRATCH_B1
jmp _remainder
bne _copyshort
rts
}}
}

View File

@ -19,14 +19,19 @@ sub clear_screen() {
txt.chrout(147)
}
sub home() {
txt.chrout(19)
}
sub nl() {
txt.chrout('\n')
}
sub home() {
txt.chrout(19)
sub spc() {
txt.chrout(' ')
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color.
; (assumes screen and color matrix are at their default addresses)

View File

@ -128,50 +128,50 @@ cx16 {
; VERA registers
const uword VERA_BASE = $9F20
&ubyte VERA_ADDR_L = VERA_BASE + $0000
&ubyte VERA_ADDR_M = VERA_BASE + $0001
&ubyte VERA_ADDR_H = VERA_BASE + $0002
&ubyte VERA_DATA0 = VERA_BASE + $0003
&ubyte VERA_DATA1 = VERA_BASE + $0004
&ubyte VERA_CTRL = VERA_BASE + $0005
&ubyte VERA_IEN = VERA_BASE + $0006
&ubyte VERA_ISR = VERA_BASE + $0007
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
const uword VERA_BASE = $9F20
&ubyte VERA_ADDR_L = VERA_BASE + $0000
&ubyte VERA_ADDR_M = VERA_BASE + $0001
&ubyte VERA_ADDR_H = VERA_BASE + $0002
&ubyte VERA_DATA0 = VERA_BASE + $0003
&ubyte VERA_DATA1 = VERA_BASE + $0004
&ubyte VERA_CTRL = VERA_BASE + $0005
&ubyte VERA_IEN = VERA_BASE + $0006
&ubyte VERA_ISR = VERA_BASE + $0007
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
; VERA_PSG_BASE = $1F9C0
; VERA_PALETTE_BASE = $1FA00
; VERA_SPRITES_BASE = $1FC00
; I/O
const uword via1 = $9f60 ;VIA 6522 #1
const uword via1 = $9f60 ;VIA 6522 #1
&ubyte d1prb = via1+0
&ubyte d1pra = via1+1
&ubyte d1ddrb = via1+2
@ -189,7 +189,7 @@ cx16 {
&ubyte d1ier = via1+14
&ubyte d1ora = via1+15
const uword via2 = $9f70 ;VIA 6522 #2
const uword via2 = $9f70 ;VIA 6522 #2
&ubyte d2prb = via2+0
&ubyte d2pra = via2+1
&ubyte d2ddrb = via2+2
@ -289,6 +289,23 @@ romsub $fecc = monitor() clobbers(A,X,Y)
; ---- utilities -----
inline asmsub rombank(ubyte rombank @A) {
; -- set the rom banks
%asm {{
sta $01 ; rom bank register (new)
sta cx16.d1prb ; rom bank register (old)
}}
}
inline asmsub rambank(ubyte rambank @A) {
; -- set the ram bank
%asm {{
sta $00 ; ram bank register (new)
sta cx16.d1pra ; ram bank register (old)
}}
}
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; -- get a byte from VERA's video memory
; note: inefficient when reading multiple sequential bytes!
@ -480,9 +497,8 @@ sys {
; Soft-reset the system back to Basic prompt.
%asm {{
sei
lda #14
sta $01
stz cx16.d1prb ; bank the kernal in
stz $01 ; bank the kernal in (new rom bank register)
stz cx16.d1prb ; bank the kernal in (old rom bank register)
jmp (cx16.RESET_VEC)
}}
}

View File

@ -19,14 +19,19 @@ sub clear_screen() {
txt.chrout(147)
}
sub home() {
txt.chrout(19)
}
sub nl() {
txt.chrout('\n')
}
sub home() {
txt.chrout(19)
sub spc() {
txt.chrout(' ')
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color.
%asm {{

View File

@ -30,7 +30,7 @@ diskio {
ubyte low = c64.CHRIN()
ubyte high = c64.CHRIN()
txt.print_uw(mkword(high, low))
txt.chrout(' ')
txt.spc()
ubyte @zp char
repeat {
char = c64.CHRIN()
@ -72,7 +72,7 @@ io_error:
str list_filename = "?" * 32
; ----- get a list of files (uses iteration functions internally -----
; ----- get a list of files (uses iteration functions internally) -----
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
@ -295,7 +295,8 @@ _in_buffer sta $ffff
; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character (or EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
; The length of the line is returned in Y.
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
; I/O error status should be checked by the caller itself via READST() routine.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@ -423,10 +424,9 @@ io_error:
sub delete(ubyte drivenumber, uword filenameptr) {
; -- delete a file on the drive
ubyte flen = string.length(filenameptr)
filename[0] = 's'
filename[1] = ':'
sys.memcopy(filenameptr, &filename+2, flen+1)
ubyte flen = string.copy(filenameptr, &filename+2)
c64.SETNAM(flen+2, filename)
c64.SETLFS(1, drivenumber, 15)
void c64.OPEN()
@ -436,13 +436,11 @@ io_error:
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive
ubyte flen_old = string.length(oldfileptr)
ubyte flen_new = string.length(newfileptr)
filename[0] = 'r'
filename[1] = ':'
sys.memcopy(newfileptr, &filename+2, flen_new)
ubyte flen_new = string.copy(newfileptr, &filename+2)
filename[flen_new+2] = '='
sys.memcopy(oldfileptr, &filename+3+flen_new, flen_old+1)
ubyte flen_old = string.copy(oldfileptr, &filename+3+flen_new)
c64.SETNAM(3+flen_new+flen_old, filename)
c64.SETLFS(1, drivenumber, 15)
void c64.OPEN()

View File

@ -1 +1 @@
6.0-BETA
6.0

View File

@ -81,6 +81,24 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program.namespace)
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
// rewrite pointervar[index] into @(pointervar+index)
val indexer = arrayIndexedExpression.indexer
val index = (indexer.indexNum ?: indexer.indexVar)!!
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
return if(parent is AssignTarget) {
// we're part of the target of an assignment, we have to actually change the assign target itself
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
} else {
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
}
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
is NumericLiteralValue -> {
arrayIndexedExpression.indexer.indexNum = expr2

View File

@ -526,40 +526,35 @@ internal class AsmGen(private val program: Program,
val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!!
val scopedName = vardecl.makeScopedName(vardecl.name)
return if(scopedName in allocatedZeropageVariables) {
// pointervar is already in the zero page, no need to copy
out(" ldy #0 | lda ($sourceName),y")
Pair(true, sourceName)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
return if (isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
out(" lda ($sourceName)")
Pair(true, sourceName)
} else {
out("""
lda $sourceName
ldy $sourceName+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda (P8ZP_SCRATCH_W1)""")
Pair(false, sourceName)
}
} else {
out("""
lda $sourceName
ldy $sourceName+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y""")
Pair(false, sourceName)
}
}
fun storeByteIntoPointer(pointervar: IdentifierReference, ldaInstructionArg: String?) {
val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!!
val scopedName = vardecl.makeScopedName(vardecl.name)
if(scopedName in allocatedZeropageVariables) {
// pointervar is already in the zero page, no need to copy
if(ldaInstructionArg!=null)
out(" lda $ldaInstructionArg")
out(" ldy #0 | sta ($sourceName),y")
} else {
out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
${if(ldaInstructionArg==null) "" else "lda $ldaInstructionArg"}
ldy #0
sta (P8ZP_SCRATCH_W2),y""")
return if (isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
out(" ldy #0 | lda ($sourceName),y")
Pair(true, sourceName)
} else {
out("""
lda $sourceName
ldy $sourceName+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y""")
Pair(false, sourceName)
}
}
}
@ -1329,4 +1324,92 @@ $label nop""")
else -> throw AssemblyError("need byte type")
}
}
internal fun isZpVar(scopedName: String): Boolean = scopedName in allocatedZeropageVariables
internal fun isZpVar(variable: IdentifierReference): Boolean {
val vardecl = variable.targetVarDecl(program.namespace)!!
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.inferType(program)
val rightDt = pointerOffsetExpr.left.inferType(program)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
if(leftDt.istype(DataType.UBYTE) && rightDt.istype(DataType.UWORD))
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UWORD)) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = pointerOffsetExpr.right.constValue(program)
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
return Pair(pointerOffsetExpr.left, NumericLiteralValue(DataType.UBYTE, constIdx.number, constIdx.position))
}
// could be that the index was type casted into uword, check that
val rightTc = pointerOffsetExpr.right as? TypecastExpression
if(rightTc!=null && rightTc.expression.inferType(program).istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.left, rightTc.expression)
val leftTc = pointerOffsetExpr.left as? TypecastExpression
if(leftTc!=null && leftTc.expression.inferType(program).istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.right, leftTc.expression)
}
}
return null
}
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? IdentifierReference
if(write) {
when(ptrAndIndex.second) {
is NumericLiteralValue, is IdentifierReference -> {
if(pointervar!=null && isZpVar(pointervar)) {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" sta (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" sta (P8ZP_SCRATCH_W2),y")
}
}
else -> {
// same as above but we need to save the A register
if(pointervar!=null && isZpVar(pointervar)) {
out(" pha")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pla")
out(" sta (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
out(" pha")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
}
}
}
} else {
if(pointervar!=null && isZpVar(pointervar)) {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y")
}
}
return true
}
}
return false
}
}

View File

@ -240,11 +240,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" ror ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff,x ; modified""")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
}
}
}
is IdentifierReference -> {
@ -329,11 +341,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" rol ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff,x ; modified""")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
}
}
}
is IdentifierReference -> {
@ -510,6 +534,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// optimized simple case: swap two memory locations
if(first is DirectMemoryRead && second is DirectMemoryRead) {
// TODO optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null

View File

@ -1457,6 +1457,24 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
internal fun translateDirectMemReadExpression(expr: DirectMemoryRead, pushResultOnEstack: Boolean) {
fun assignViaExprEval() {
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
if (pushResultOnEstack) {
asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
}
} else {
if (pushResultOnEstack) {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
}
}
}
when(expr.addressExpression) {
is NumericLiteralValue -> {
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
@ -1470,14 +1488,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
else -> {
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if(pushResultOnEstack) {
asmgen.out(" dex | ldy #0 | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x")
is BinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(expr.addressExpression as BinaryExpression, false)) {
if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignViaExprEval()
}
}
else -> assignViaExprEval()
}
}

View File

@ -112,6 +112,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
SourceStorageKind.MEMORY -> {
fun assignViaExprEval(expression: Expression) {
assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(assign.target, CpuRegister.A)
}
val value = assign.source.memory!!
when (value.addressExpression) {
is NumericLiteralValue -> {
@ -121,11 +130,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is IdentifierReference -> {
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
}
else -> {
assignExpressionToVariable(value.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(assign.target, CpuRegister.A)
is BinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.addressExpression)
}
}
else -> assignViaExprEval(value.addressExpression)
}
}
SourceStorageKind.EXPRESSION -> {
@ -297,14 +309,37 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
is DirectMemoryRead -> {
if(targetDt in WordDatatypes) {
if (value.addressExpression is NumericLiteralValue) {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
assignMemoryByteIntoWord(target, address, null)
return
fun assignViaExprEval(addressExpression: Expression) {
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(target, CpuRegister.A)
}
else if (value.addressExpression is IdentifierReference) {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
return
when (value.addressExpression) {
is NumericLiteralValue -> {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
assignMemoryByteIntoWord(target, address, null)
return
}
is IdentifierReference -> {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
return
}
is BinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
assignViaExprEval(value.addressExpression)
}
}
else -> {
assignViaExprEval(value.addressExpression)
}
}
}
}
@ -715,8 +750,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> {
asmgen.out(" inx")
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
asmgen.out(" inx | lda P8ESTACK_LO,x")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(target.constArrayIndexValue!=null) {
@ -1144,7 +1179,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
asmgen.out(" lda $sourceName")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1304,7 +1340,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
}
TargetStorageKind.MEMORY -> {
storeRegisterInMemoryAddress(register, target.memory!!)
when(register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1574,7 +1615,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" stz ${target.asmVarname} ")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
asmgen.out(" lda #${byte.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1613,7 +1655,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname} ")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
asmgen.out(" lda #${byte.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1797,7 +1840,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress(address.toHex(), target.memory!!)
asmgen.out(" lda ${address.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${target.asmVarname}")
@ -1834,8 +1878,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" sta ${target.asmVarname}")
}
TargetStorageKind.MEMORY -> {
val sourceName = asmgen.asmVariableName(identifier)
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
asmgen.loadByteFromPointerIntoA(identifier)
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${target.asmVarname} ")
@ -1931,46 +1975,76 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
when {
addressLv != null -> {
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
}
else -> {
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" ldy #0 | lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2),y")
fun storeViaExprEval() {
when(addressExpr) {
is NumericLiteralValue, is IdentifierReference -> {
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
else -> {
// same as above but we need to save the A register
asmgen.out(" pha")
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" pla")
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
}
}
fun storeAIntoPointerVar(pointervar: IdentifierReference) {
val sourceName = asmgen.asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!!
val scopedName = vardecl.makeScopedName(vardecl.name)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
if (asmgen.isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
asmgen.out(" sta ($sourceName)")
} else {
asmgen.out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
sta (P8ZP_SCRATCH_W2)""")
}
} else {
if (asmgen.isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
asmgen.out(" ldy #0 | sta ($sourceName),y")
} else {
asmgen.out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
ldy #0
sta (P8ZP_SCRATCH_W2),y""")
}
}
}
}
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
// this is optimized for register A.
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
val registerName = register.name.toLowerCase()
when {
addressLv != null -> {
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
asmgen.out(" sta ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.storeByteIntoPointer(addressExpr, null)
storeAIntoPointerVar(addressExpr)
}
else -> {
asmgen.saveRegisterStack(register, false)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
addressExpr is BinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
storeViaExprEval()
}
else -> storeViaExprEval()
}
}

View File

@ -102,6 +102,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof),
// normal functions follow:
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
@ -278,6 +279,28 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
}
}
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
// 1 arg, type = anything, result type = ubyte
if(args.size!=1)
throw SyntaxError("offsetof requires one argument", position)
val idref = args[0] as? IdentifierReference
?: throw SyntaxError("offsetof argument should be an identifier", position)
val vardecl = idref.targetVarDecl(program.namespace)!!
val struct = vardecl.struct
if (struct == null || vardecl.datatype == DataType.STRUCT)
throw SyntaxError("offsetof can only be used on struct members", position)
val membername = idref.nameInSource.last()
var offset = 0
for(member in struct.statements) {
if((member as VarDecl).name == membername)
return NumericLiteralValue(DataType.UBYTE, offset, position)
offset += member.datatype.memorySize()
}
throw SyntaxError("undefined struct member", position)
}
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
// 1 arg, type = anything, result type = ubyte
if(args.size!=1)

View File

@ -381,13 +381,20 @@ Direct access to memory locations
Normally memory locations are accessed by a *memory mapped* name, such as ``c64.BGCOL0`` that is defined
as the memory mapped address $d021.
If you want to access a memory location directly (by using the address itself), without defining
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
If you want to access a memory location directly (by using the address itself or via an uword pointer variable),
without defining a memory mapped location, you can do so by enclosing the address in ``@(...)``::
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
This is the official syntax to 'dereference a pointer' as it is often named in other languages.
You can actually also use the array indexing notation for this. It will be silently converted into
the direct memory access expression as explained above. Note that this also means that unlike regular arrays,
the index is not limited to an ubyte value. You can use a full uword to index a pointer variable like this::
pointervar[999] = 0 ; set memory byte to zero at location pointervar + 999.
Converting types into other types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -836,6 +843,11 @@ sizeof(name)
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
Note: usually you will be interested in the number of elements in an array, use len() for that.
offsetof(membername)
Number of bytes from the start of a struct variable that this member variable is located.
For now, this only works on members of a declared struct variable and not yet on members
referenced from the struct type itself. This might be improved in a future version of the language.
swap(x, y)
Swap the values of numerical variables (or memory locations) x and y in a fast way.

View File

@ -335,6 +335,10 @@ directly access the memory. Enclose a numeric expression or literal with ``@(...
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
The array indexing notation is syntactic sugar for such a direct memory access expression::
pointervar[999] = 0 ; equivalent to @(pointervar+999) = 0
Constants
^^^^^^^^^

View File

@ -2,15 +2,23 @@
TODO
====
- use (zp) addressing mode on 65c02 specific code rather than ldy#0 / lda (zp),y
- optimize pointer access code @(pointer)? use a subroutine? macro? 65c02 vs 6502?
- add any2(), all2(), max2(), min2(), reverse2(), sum2(), sort2() that take (array, startindex, length) arguments
- optimize for loop iterations better to allow proper inx, cpx #value, bne loop instructions (like repeat loop)
- why is there a beq _prog8_label_2_repeatend at the end of repeat loops? seems unused
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- implement the linked_list millfork benchmark
- use the 65c02 bit clear/set/test instructions for single-bit operations
- implement highres 4 color mode in gfx2
- make a retro amiga workbench 1.3 and/or 2.0 workbench "simulator" using that new gfx mode
- can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50
- add a compiler option to generate a symbol listing at the end
- add a f_seek() routine for the Cx16 that uses its seek dos api?
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
- add a compiler option to not remove unused subroutines. this allows for building library programs
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- c64: use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
- c64: make the graphics.BITMAP_ADDRESS configurable
- some support for recursive subroutines?
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables

View File

@ -1,496 +0,0 @@
from collections import Counter
from enum import IntEnum
class AddrMode(IntEnum):
Imp = 1,
Acc = 2,
Imm = 3,
Zp = 4,
ZpX = 5,
ZpY = 6,
Rel = 7,
Abs = 8,
AbsX = 9,
AbsY = 10,
Ind = 11,
IzX = 12,
IzY = 13,
Zpr = 14,
Izp = 15,
IaX = 16
AllInstructions = [
(0x00, "brk", AddrMode.Imp),
(0x01, "ora", AddrMode.IzX),
(0x02, "nop", AddrMode.Imm),
(0x03, "nop", AddrMode.Imp),
(0x04, "tsb", AddrMode.Zp),
(0x05, "ora", AddrMode.Zp),
(0x06, "asl", AddrMode.Zp),
(0x07, "rmb0", AddrMode.Zp),
(0x08, "php", AddrMode.Imp),
(0x09, "ora", AddrMode.Imm),
(0x0a, "asl", AddrMode.Acc),
(0x0b, "nop", AddrMode.Imp),
(0x0c, "tsb", AddrMode.Abs),
(0x0d, "ora", AddrMode.Abs),
(0x0e, "asl", AddrMode.Abs),
(0x0f, "bbr0", AddrMode.Zpr),
(0x10, "bpl", AddrMode.Rel),
(0x11, "ora", AddrMode.IzY),
(0x12, "ora", AddrMode.Izp),
(0x13, "nop", AddrMode.Imp),
(0x14, "trb", AddrMode.Zp),
(0x15, "ora", AddrMode.ZpX),
(0x16, "asl", AddrMode.ZpX),
(0x17, "rmb1", AddrMode.Zp),
(0x18, "clc", AddrMode.Imp),
(0x19, "ora", AddrMode.AbsY),
(0x1a, "inc", AddrMode.Acc),
(0x1b, "nop", AddrMode.Imp),
(0x1c, "trb", AddrMode.Abs),
(0x1d, "ora", AddrMode.AbsX),
(0x1e, "asl", AddrMode.AbsX),
(0x1f, "bbr1", AddrMode.Zpr),
(0x20, "jsr", AddrMode.Abs),
(0x21, "and", AddrMode.IzX),
(0x22, "nop", AddrMode.Imm),
(0x23, "nop", AddrMode.Imp),
(0x24, "bit", AddrMode.Zp),
(0x25, "and", AddrMode.Zp),
(0x26, "rol", AddrMode.Zp),
(0x27, "rmb2", AddrMode.Zp),
(0x28, "plp", AddrMode.Imp),
(0x29, "and", AddrMode.Imm),
(0x2a, "rol", AddrMode.Acc),
(0x2b, "nop", AddrMode.Imp),
(0x2c, "bit", AddrMode.Abs),
(0x2d, "and", AddrMode.Abs),
(0x2e, "rol", AddrMode.Abs),
(0x2f, "bbr2", AddrMode.Zpr),
(0x30, "bmi", AddrMode.Rel),
(0x31, "and", AddrMode.IzY),
(0x32, "and", AddrMode.Izp),
(0x33, "nop", AddrMode.Imp),
(0x34, "bit", AddrMode.ZpX),
(0x35, "and", AddrMode.ZpX),
(0x36, "rol", AddrMode.ZpX),
(0x37, "rmb3", AddrMode.Zp),
(0x38, "sec", AddrMode.Imp),
(0x39, "and", AddrMode.AbsY),
(0x3a, "dec", AddrMode.Acc),
(0x3b, "nop", AddrMode.Imp),
(0x3c, "bit", AddrMode.AbsX),
(0x3d, "and", AddrMode.AbsX),
(0x3e, "rol", AddrMode.AbsX),
(0x3f, "bbr3", AddrMode.Zpr),
(0x40, "rti", AddrMode.Imp),
(0x41, "eor", AddrMode.IzX),
(0x42, "nop", AddrMode.Imm),
(0x43, "nop", AddrMode.Imp),
(0x44, "nop", AddrMode.Zp),
(0x45, "eor", AddrMode.Zp),
(0x46, "lsr", AddrMode.Zp),
(0x47, "rmb4", AddrMode.Zp),
(0x48, "pha", AddrMode.Imp),
(0x49, "eor", AddrMode.Imm),
(0x4a, "lsr", AddrMode.Acc),
(0x4b, "nop", AddrMode.Imp),
(0x4c, "jmp", AddrMode.Abs),
(0x4d, "eor", AddrMode.Abs),
(0x4e, "lsr", AddrMode.Abs),
(0x4f, "bbr4", AddrMode.Zpr),
(0x50, "bvc", AddrMode.Rel),
(0x51, "eor", AddrMode.IzY),
(0x52, "eor", AddrMode.Izp),
(0x53, "nop", AddrMode.Imp),
(0x54, "nop", AddrMode.ZpX),
(0x55, "eor", AddrMode.ZpX),
(0x56, "lsr", AddrMode.ZpX),
(0x57, "rmb5", AddrMode.Zp),
(0x58, "cli", AddrMode.Imp),
(0x59, "eor", AddrMode.AbsY),
(0x5a, "phy", AddrMode.Imp),
(0x5b, "nop", AddrMode.Imp),
(0x5c, "nop", AddrMode.Abs),
(0x5d, "eor", AddrMode.AbsX),
(0x5e, "lsr", AddrMode.AbsX),
(0x5f, "bbr5", AddrMode.Zpr),
(0x60, "rts", AddrMode.Imp),
(0x61, "adc", AddrMode.IzX),
(0x62, "nop", AddrMode.Imm),
(0x63, "nop", AddrMode.Imp),
(0x64, "stz", AddrMode.Zp),
(0x65, "adc", AddrMode.Zp),
(0x66, "ror", AddrMode.Zp),
(0x67, "rmb6", AddrMode.Zp),
(0x68, "pla", AddrMode.Imp),
(0x69, "adc", AddrMode.Imm),
(0x6a, "ror", AddrMode.Acc),
(0x6b, "nop", AddrMode.Imp),
(0x6c, "jmp", AddrMode.Ind),
(0x6d, "adc", AddrMode.Abs),
(0x6e, "ror", AddrMode.Abs),
(0x6f, "bbr6", AddrMode.Zpr),
(0x70, "bvs", AddrMode.Rel),
(0x71, "adc", AddrMode.IzY),
(0x72, "adc", AddrMode.Izp),
(0x73, "nop", AddrMode.Imp),
(0x74, "stz", AddrMode.ZpX),
(0x75, "adc", AddrMode.ZpX),
(0x76, "ror", AddrMode.ZpX),
(0x77, "rmb7", AddrMode.Zp),
(0x78, "sei", AddrMode.Imp),
(0x79, "adc", AddrMode.AbsY),
(0x7a, "ply", AddrMode.Imp),
(0x7b, "nop", AddrMode.Imp),
(0x7c, "jmp", AddrMode.IaX),
(0x7d, "adc", AddrMode.AbsX),
(0x7e, "ror", AddrMode.AbsX),
(0x7f, "bbr7", AddrMode.Zpr),
(0x80, "bra", AddrMode.Rel),
(0x81, "sta", AddrMode.IzX),
(0x82, "nop", AddrMode.Imm),
(0x83, "nop", AddrMode.Imp),
(0x84, "sty", AddrMode.Zp),
(0x85, "sta", AddrMode.Zp),
(0x86, "stx", AddrMode.Zp),
(0x87, "smb0", AddrMode.Zp),
(0x88, "dey", AddrMode.Imp),
(0x89, "bit", AddrMode.Imm),
(0x8a, "txa", AddrMode.Imp),
(0x8b, "nop", AddrMode.Imp),
(0x8c, "sty", AddrMode.Abs),
(0x8d, "sta", AddrMode.Abs),
(0x8e, "stx", AddrMode.Abs),
(0x8f, "bbs0", AddrMode.Zpr),
(0x90, "bcc", AddrMode.Rel),
(0x91, "sta", AddrMode.IzY),
(0x92, "sta", AddrMode.Izp),
(0x93, "nop", AddrMode.Imp),
(0x94, "sty", AddrMode.ZpX),
(0x95, "sta", AddrMode.ZpX),
(0x96, "stx", AddrMode.ZpY),
(0x97, "smb1", AddrMode.Zp),
(0x98, "tya", AddrMode.Imp),
(0x99, "sta", AddrMode.AbsY),
(0x9a, "txs", AddrMode.Imp),
(0x9b, "nop", AddrMode.Imp),
(0x9c, "stz", AddrMode.Abs),
(0x9d, "sta", AddrMode.AbsX),
(0x9e, "stz", AddrMode.AbsX),
(0x9f, "bbs1", AddrMode.Zpr),
(0xa0, "ldy", AddrMode.Imm),
(0xa1, "lda", AddrMode.IzX),
(0xa2, "ldx", AddrMode.Imm),
(0xa3, "nop", AddrMode.Imp),
(0xa4, "ldy", AddrMode.Zp),
(0xa5, "lda", AddrMode.Zp),
(0xa6, "ldx", AddrMode.Zp),
(0xa7, "smb2", AddrMode.Zp),
(0xa8, "tay", AddrMode.Imp),
(0xa9, "lda", AddrMode.Imm),
(0xaa, "tax", AddrMode.Imp),
(0xab, "nop", AddrMode.Imp),
(0xac, "ldy", AddrMode.Abs),
(0xad, "lda", AddrMode.Abs),
(0xae, "ldx", AddrMode.Abs),
(0xaf, "bbs2", AddrMode.Zpr),
(0xb0, "bcs", AddrMode.Rel),
(0xb1, "lda", AddrMode.IzY),
(0xb2, "lda", AddrMode.Izp),
(0xb3, "nop", AddrMode.Imp),
(0xb4, "ldy", AddrMode.ZpX),
(0xb5, "lda", AddrMode.ZpX),
(0xb6, "ldx", AddrMode.ZpY),
(0xb7, "smb3", AddrMode.Zp),
(0xb8, "clv", AddrMode.Imp),
(0xb9, "lda", AddrMode.AbsY),
(0xba, "tsx", AddrMode.Imp),
(0xbb, "nop", AddrMode.Imp),
(0xbc, "ldy", AddrMode.AbsX),
(0xbd, "lda", AddrMode.AbsX),
(0xbe, "ldx", AddrMode.AbsY),
(0xbf, "bbs3", AddrMode.Zpr),
(0xc0, "cpy", AddrMode.Imm),
(0xc1, "cmp", AddrMode.IzX),
(0xc2, "nop", AddrMode.Imm),
(0xc3, "nop", AddrMode.Imp),
(0xc4, "cpy", AddrMode.Zp),
(0xc5, "cmp", AddrMode.Zp),
(0xc6, "dec", AddrMode.Zp),
(0xc7, "smb4", AddrMode.Zp),
(0xc8, "iny", AddrMode.Imp),
(0xc9, "cmp", AddrMode.Imm),
(0xca, "dex", AddrMode.Imp),
(0xcb, "wai", AddrMode.Imp),
(0xcc, "cpy", AddrMode.Abs),
(0xcd, "cmp", AddrMode.Abs),
(0xce, "dec", AddrMode.Abs),
(0xcf, "bbs4", AddrMode.Zpr),
(0xd0, "bne", AddrMode.Rel),
(0xd1, "cmp", AddrMode.IzY),
(0xd2, "cmp", AddrMode.Izp),
(0xd3, "nop", AddrMode.Imp),
(0xd4, "nop", AddrMode.ZpX),
(0xd5, "cmp", AddrMode.ZpX),
(0xd6, "dec", AddrMode.ZpX),
(0xd7, "smb5", AddrMode.Zp),
(0xd8, "cld", AddrMode.Imp),
(0xd9, "cmp", AddrMode.AbsY),
(0xda, "phx", AddrMode.Imp),
(0xdb, "stp", AddrMode.Imp),
(0xdc, "nop", AddrMode.Abs),
(0xdd, "cmp", AddrMode.AbsX),
(0xde, "dec", AddrMode.AbsX),
(0xdf, "bbs5", AddrMode.Zpr),
(0xe0, "cpx", AddrMode.Imm),
(0xe1, "sbc", AddrMode.IzX),
(0xe2, "nop", AddrMode.Imm),
(0xe3, "nop", AddrMode.Imp),
(0xe4, "cpx", AddrMode.Zp),
(0xe5, "sbc", AddrMode.Zp),
(0xe6, "inc", AddrMode.Zp),
(0xe7, "smb6", AddrMode.Zp),
(0xe8, "inx", AddrMode.Imp),
(0xe9, "sbc", AddrMode.Imm),
(0xea, "nop", AddrMode.Imp),
(0xeb, "nop", AddrMode.Imp),
(0xec, "cpx", AddrMode.Abs),
(0xed, "sbc", AddrMode.Abs),
(0xee, "inc", AddrMode.Abs),
(0xef, "bbs6", AddrMode.Zpr),
(0xf0, "beq", AddrMode.Rel),
(0xf1, "sbc", AddrMode.IzY),
(0xf2, "sbc", AddrMode.Izp),
(0xf3, "nop", AddrMode.Imp),
(0xf4, "nop", AddrMode.ZpX),
(0xf5, "sbc", AddrMode.ZpX),
(0xf6, "inc", AddrMode.ZpX),
(0xf7, "smb7", AddrMode.Zp),
(0xf8, "sed", AddrMode.Imp),
(0xf9, "sbc", AddrMode.AbsY),
(0xfa, "plx", AddrMode.Imp),
(0xfb, "nop", AddrMode.Imp),
(0xfc, "nop", AddrMode.AbsX),
(0xfd, "sbc", AddrMode.AbsX),
(0xfe, "inc", AddrMode.AbsX),
(0xff, "bbs7", AddrMode.Zpr)
]
# NOP is weird, it is all over the place.
# For the 'common' immediate NOP, keep only the $EA opcode (this was the original NOP on the 6502)
Instructions = [ins for ins in AllInstructions if ins[1] != "nop"] + [(0xea, "nop", AddrMode.Imp)]
InstructionsByName = {}
for ins in Instructions:
if ins[1] not in InstructionsByName:
InstructionsByName[ins[1]] = {ins[2]: ins[0]}
else:
InstructionsByName[ins[1]][ins[2]] = ins[0]
InstructionsByMode = {}
for ins in Instructions:
if ins[2] not in InstructionsByMode:
InstructionsByMode[ins[2]] = [(ins[1], ins[0])]
else:
InstructionsByMode[ins[2]].append((ins[1], ins[0]))
# build the name->modes table
print("; generated by opcodes.py")
print("; addressing modes:")
for mode in AddrMode:
print(";", mode.value, "=", mode.name)
print()
print("""
.enc "petscii" ;define an ascii to petscii encoding
.cdef " @", 32 ;characters
.cdef "AZ", $c1
.cdef "az", $41
.cdef "[[", $5b
.cdef "]]", $5d
.edef "<nothing>", [];replace with no bytes
""")
for instr in sorted(InstructionsByName.items()):
print("i_" + instr[0] + ":\n\t.byte ", end="")
if len(instr[1]) == 1:
# many instructions have just 1 addressing mode, save space for those
info = instr[1].popitem()
print("1,", info[0].value,",", info[1])
else:
print("0, ", end='')
mode_opcodes = []
for mode in AddrMode:
if mode in instr[1]:
mode_opcodes.append(instr[1][mode])
else:
mode_opcodes.append(0)
print(",".join(str(o) for o in mode_opcodes), end="")
print()
def determine_mnemonics():
mnemonics = list(sorted(set(ins[1] for ins in Instructions)))
# opcodes histogram (ordered by occurrence) (in kernal + basic roms of the c64):
opcode_occurrences = [
(32, 839), (133, 502), (165, 488), (0, 429), (208, 426), (169, 390), (76, 324), (240, 322), (2, 314), (160, 245),
(96, 228), (3, 201), (1, 191), (255, 186), (144, 182), (170, 175), (162, 169), (177, 165), (104, 159), (164, 158),
(132, 157), (201, 156), (72, 151), (141, 150), (200, 146), (173, 144), (166, 139), (176, 139), (16, 138),
(134, 138), (73, 127), (24, 119), (101, 113), (69, 109), (13, 107), (34, 104), (145, 103), (4, 102), (168, 101),
(221, 98), (230, 93), (48, 91), (189, 87), (41, 86), (6, 86), (9, 86), (8, 85), (79, 85), (138, 80), (10, 80),
(7, 79), (185, 77), (56, 75), (44, 75), (78, 74), (105, 73), (5, 73), (174, 73), (220, 71), (198, 69), (232, 69),
(36, 69), (202, 67), (152, 67), (95, 67), (100, 65), (102, 65), (247, 65), (188, 64), (136, 64), (84, 64),
(122, 62), (128, 61), (80, 61), (186, 60), (82, 59), (97, 58), (15, 57), (70, 57), (229, 56), (19, 55), (40, 54),
(183, 54), (65, 54), (233, 53), (180, 53), (12, 53), (171, 53), (197, 53), (83, 52), (248, 52), (112, 51),
(237, 51), (89, 50), (11, 50), (158, 50), (74, 49), (224, 48), (20, 47), (238, 47), (108, 46), (234, 46),
(251, 46), (254, 46), (184, 45), (14, 44), (163, 44), (226, 43), (211, 43), (88, 43), (98, 42), (17, 42),
(153, 42), (243, 41), (228, 41), (99, 41), (253, 41), (209, 41), (187, 39), (123, 39), (67, 39), (196, 38),
(68, 38), (35, 38), (172, 38), (175, 38), (161, 38), (85, 38), (191, 37), (113, 37), (182, 37), (151, 37),
(71, 36), (181, 35), (214, 35), (121, 35), (157, 35), (178, 35), (77, 35), (42, 34), (212, 33), (18, 33),
(127, 33), (241, 33), (21, 33), (249, 32), (23, 31), (245, 30), (142, 30), (55, 29), (140, 29), (46, 29),
(192, 29), (179, 29), (252, 29), (115, 29), (22, 29), (43, 28), (215, 28), (45, 28), (246, 28), (38, 28),
(86, 27), (225, 27), (25, 26), (239, 26), (58, 26), (167, 26), (147, 26), (217, 26), (149, 25), (30, 25),
(206, 25), (28, 24), (47, 24), (37, 24), (155, 24), (129, 23), (148, 23), (111, 23), (29, 23), (39, 23),
(51, 22), (193, 22), (236, 22), (120, 22), (64, 22), (204, 21), (210, 21), (244, 21), (52, 21), (66, 21),
(114, 20), (250, 20), (106, 20), (93, 19), (199, 19), (218, 19), (154, 19), (205, 19), (50, 19), (159, 19),
(194, 19), (49, 19), (190, 19), (103, 18), (216, 18), (213, 18), (107, 18), (131, 18), (63, 18), (94, 18),
(91, 17), (242, 17), (109, 17), (53, 16), (227, 16), (139, 16), (31, 16), (75, 16), (60, 16), (195, 15),
(231, 15), (62, 15), (59, 15), (87, 14), (207, 14), (27, 14), (90, 14), (110, 13), (223, 13), (57, 13),
(118, 12), (26, 12), (203, 12), (81, 12), (156, 12), (54, 12), (235, 12), (146, 11), (135, 11), (126, 11),
(150, 11), (130, 11), (143, 10), (61, 10), (219, 10), (124, 9), (222, 9), (125, 9), (119, 7), (137, 7),
(33, 7), (117, 5), (92, 4), (116, 3)
]
cnt = Counter()
for opcode, amount in opcode_occurrences:
cnt[AllInstructions[opcode][1]] += amount
cnt["nop"] = 13
cnt["tsb"] = 13
four_letter_mnemonics = list(sorted([ins[1] for ins in AllInstructions if len(ins[1])>3]))
for ins4 in four_letter_mnemonics:
del cnt[ins4]
cnt[ins4] = 1
mnem2 = [c[0] for c in cnt.most_common()]
if len(mnem2)!=len(mnemonics):
raise ValueError("mnem count mismatch")
return mnem2
mnemonics = determine_mnemonics()
def first_letters():
firstletters = {m[0]: 0 for m in mnemonics}
return firstletters.keys()
def second_letters(firstletter):
secondletters = {m[1]: 0 for m in mnemonics if m[0] == firstletter}
return secondletters.keys()
def third_letters(firstletter, secondletter):
thirdletters = {m[2]: 0 for m in mnemonics if m[0] == firstletter and m[1] == secondletter}
return thirdletters.keys()
def fourth_letters(firstletter, secondletter, thirdletter):
longmnem = [m for m in mnemonics if len(m) > 3]
fourthletters = {m[3]: 0 for m in longmnem if m[0] == firstletter and m[1] == secondletter and m[2] == thirdletter}
return fourthletters.keys()
def make_tree():
tree = {}
for first in first_letters():
tree[first] = {
secondletter: {
thirdletter: {
fourthletter: {}
for fourthletter in fourth_letters(first, secondletter, thirdletter)
}
for thirdletter in third_letters(first, secondletter)
}
for secondletter in second_letters(first)
}
return tree
tree = make_tree()
print("get_opcode_info .proc")
print("_mnem_fourth_letter = cx16.r4")
print("_mnem_fifth_letter = cx16.r5")
for first in tree:
print(" cmp #'%s'" % first)
print(" bne _not_%s" % first)
for second in tree[first]:
print(" cpx #'%s'" % second)
print(" bne _not_%s%s" % (first,second))
for third in tree[first][second]:
print(" cpy #'%s'" % third)
print(" bne _not_%s%s%s" % (first, second, third))
fourth = tree[first][second][third]
if fourth:
if "".join(fourth.keys()) != "01234567":
raise ValueError("fourth", fourth.keys())
print(" bra _check_%s%s%s" % (first, second, third))
else:
print(" lda _mnem_fourth_letter") # check that the fourth letter is not present
print(" bne _invalid")
print(" lda #<i_%s%s%s" % (first, second, third))
print(" ldy #>i_%s%s%s" % (first, second, third))
print(" rts")
print("_not_%s%s%s:" % (first, second, third))
print("_not_%s%s:" % (first, second))
print("_not_%s:" % first)
print("_invalid:")
print(" lda #0")
print(" ldy #0")
print(" rts")
# the 4-letter mnemonics are:
# smb[0-7]
# bbr[0-7]
# rmb[0-7]
# bbs[0-7]
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_check_%s" % fourlettermnemonic)
print(" lda #<_tab_%s" % fourlettermnemonic)
print(" ldy #>_tab_%s" % fourlettermnemonic)
print(""" sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
bra _check4""")
print("""_check4
lda _mnem_fourth_letter
cmp #'0'
bcc _invalid
cmp #'8'
bcs _invalid
lda _mnem_fifth_letter ; must have no fifth letter
bne _invalid
tay
lda (P8ZP_SCRATCH_W2),y
pha
iny
lda (P8ZP_SCRATCH_W2),y
tay
pla
rts""")
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_tab_%s" % fourlettermnemonic)
for ii in "01234567":
print(" .word i_%s%s" % (fourlettermnemonic, ii))
print(" .pend")

View File

@ -482,7 +482,7 @@ galaxy {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet.name = make_current_planet_name()
planet.display(true)
txt.print(" (")
@ -853,9 +853,9 @@ planet {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -864,7 +864,7 @@ planet {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -883,15 +883,15 @@ planet {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -956,7 +956,7 @@ util {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy10 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet10.name = make_current_planet10_name()
planet10.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet10 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet10 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet10 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util10 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy2 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet2.name = make_current_planet2_name()
planet2.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet2 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet2 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet2 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util2 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy3 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet3.name = make_current_planet3_name()
planet3.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet3 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet3 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet3 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util3 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy4 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet4.name = make_current_planet4_name()
planet4.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet4 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet4 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet4 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util4 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy5 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet5.name = make_current_planet5_name()
planet5.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet5 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet5 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet5 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util5 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy6 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet6.name = make_current_planet6_name()
planet6.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet6 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet6 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet6 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util6 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy7 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet7.name = make_current_planet7_name()
planet7.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet7 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet7 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet7 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util7 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy8 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet8.name = make_current_planet8_name()
planet8.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet8 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet8 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet8 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util8 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -472,7 +472,7 @@ galaxy9 {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet9.name = make_current_planet9_name()
planet9.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet9 {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet9 {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet9 {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util9 {
sub print_right(ubyte width, uword st) {
repeat width - string.length(st) {
txt.chrout(' ')
txt.spc()
}
txt.print(st)
}

View File

@ -1,8 +0,0 @@
all: perfecthash.c opcodes.asm
perfecthash.c: gen_opcodes.py
python gen_opcodes.py --mnemlist | gperf --no-strlen --null-strings -7 -C -D -E -m 100 > perfecthash.c
opcodes.asm: gen_opcodes.py
python gen_opcodes.py --parser > opcodes.asm

View File

@ -1,705 +0,0 @@
%target cx16
%import test_stack
%import textio
%import diskio
%import string
%zeropage basicsafe
%option no_sysinit
; raw file loading of the large assembly file $c000-$ffff: 372 jiffies
; time loading and actually processing it: 700 jiffies
main {
sub start() {
txt.lowercase()
txt.print("\n65c02 file based assembler.\n")
; benchmar_raw_read()
; user_input()
file_input()
; test_stack.test()
}
sub benchmar_raw_read() {
str filename = "romdis.asm"
ubyte[256] buffer
if diskio.f_open(8, filename) {
c64.SETTIM(0,0,0)
txt.print(filename)
txt.print("\ntiming raw file loading..")
repeat {
uword siz= diskio.f_read(buffer, 256)
txt.chrout('.')
if not siz
break
}
diskio.f_close()
txt.print("\ntime (jiffies): ")
txt.print_uw(c64.RDTIM16())
txt.nl()
}
}
sub user_input() {
textparse.print_emit_bytes = true
txt.print("Empty line to stop.\n")
repeat {
ubyte input_length = 0
txt.chrout('A')
txt.print_uwhex(textparse.program_counter, 1)
txt.print(": ")
; simulate user always having at least one space at the start
textparse.input_line[0] = ' '
input_length = txt.input_chars(&textparse.input_line+1)
txt.nl()
if not input_length {
txt.print("exit\n")
return
}
if not textparse.process_line()
break
}
}
sub file_input() {
textparse.print_emit_bytes = false
str filename = "hello.asm"
if diskio.f_open(8, filename) {
c64.SETTIM(0,0,0)
uword line=0
txt.print(filename)
txt.print("\nassembling..")
repeat {
if diskio.f_readline(textparse.input_line) {
line++
if not lsb(line)
txt.chrout('.')
if not textparse.process_line() {
txt.print("\nerror. last line was ")
txt.print_uw(line)
txt.chrout(':')
txt.print(textparse.word_addrs[0])
txt.chrout(' ')
txt.print(textparse.word_addrs[1])
txt.chrout(' ')
txt.print(textparse.word_addrs[2])
txt.nl()
break
}
if c64.READST()
break
if c64.STOP2() {
txt.print("?break\n")
break
}
} else
break
}
diskio.f_close()
txt.print("\nlast pc: ")
txt.print_uwhex(textparse.program_counter, 1)
txt.print("\nlines: ")
txt.print_uw(line)
txt.print("\ntime (jiffies): ")
txt.print_uw(c64.RDTIM16())
txt.nl()
}
}
}
textparse {
; byte counts per address mode id:
ubyte[17] operand_size = [$ff, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 1, 2]
str input_line = "?" * 40
uword[3] word_addrs
uword program_counter = $4000
ubyte print_emit_bytes = true
sub process_line() -> ubyte {
string.lower(input_line)
preprocess_assignment_spacing()
split_input()
if word_addrs[1] and @(word_addrs[1])=='='
return do_assign()
else
return do_label_andor_instr()
return false
}
sub do_assign() -> ubyte {
; target is in word_addrs[0], value is in word_addrs[2] ('=' is in word_addrs[1])
if not word_addrs[2] {
txt.print("?syntax error\n")
return false
}
ubyte valid_operand=false
if @(word_addrs[2])=='*' {
cx16.r15 = program_counter
valid_operand = true
} else {
ubyte nlen = conv.any2uword(word_addrs[2])
valid_operand = nlen and @(word_addrs[2]+nlen)==0
}
if valid_operand {
if string.compare(word_addrs[0], "*")==0 {
program_counter = cx16.r15
txt.print("\npc set to: ")
txt.print_uwhex(program_counter, true)
txt.nl()
} else {
set_symbol(word_addrs[0], cx16.r15)
}
return true
}
txt.print("?invalid operand\n")
return false
}
sub do_label_andor_instr() -> ubyte {
uword label_ptr = 0
uword instr_ptr = 0
uword operand_ptr = 0
ubyte starts_with_whitespace = input_line[0]==' ' or input_line[0]==9 or input_line[0]==160
if word_addrs[2] {
label_ptr = word_addrs[0]
instr_ptr = word_addrs[1]
operand_ptr = word_addrs[2]
} else if word_addrs[1] {
if starts_with_whitespace {
instr_ptr = word_addrs[0]
operand_ptr = word_addrs[1]
} else {
label_ptr = word_addrs[0]
instr_ptr = word_addrs[1]
}
} else if word_addrs[0] {
if starts_with_whitespace
instr_ptr = word_addrs[0]
else
label_ptr = word_addrs[0]
}
if label_ptr {
uword lastlabelchar = label_ptr + string.length(label_ptr)-1
if @(lastlabelchar) == ':'
@(lastlabelchar) = 0
if instructions.match(label_ptr) {
txt.print("?label cannot be a mnemonic\n")
return false
}
set_symbol(label_ptr, program_counter)
}
if instr_ptr {
if @(instr_ptr)=='.'
return process_assembler_directive(instr_ptr, operand_ptr)
return assemble_instruction(instr_ptr, operand_ptr)
}
return true ; empty line
}
sub assemble_instruction(uword instr_ptr, uword operand_ptr) -> ubyte {
uword instruction_info_ptr = instructions.match(instr_ptr)
if instruction_info_ptr {
; we got a mnemonic match, now process the operand (and its value, if applicable, into cx16.r15)
ubyte addr_mode = parse_operand(operand_ptr)
if addr_mode {
ubyte opcode = instructions.opcode(instruction_info_ptr, addr_mode)
if_cc {
; most likely an invalid instruction BUT could also be a branchin instruction
; that needs its "absolute" operand recalculated as relative.
ubyte retry = false
when addr_mode {
instructions.am_Abs -> {
if @(instr_ptr)=='b' {
addr_mode = instructions.am_Rel
if not calc_relative_branch_into_r14()
return false
cx16.r15 = cx16.r14
retry = true
}
}
instructions.am_Imp -> {
addr_mode = instructions.am_Acc
retry = true
}
instructions.am_Izp -> {
addr_mode = instructions.am_Ind
retry = true
}
instructions.am_Zp -> {
addr_mode = instructions.am_Abs
retry = true
}
}
if retry
opcode = instructions.opcode(instruction_info_ptr, addr_mode)
if not opcode {
txt.print("?invalid instruction\n")
return false
}
}
if addr_mode==instructions.am_Zpr {
; instructions like BBR4 $zp,$aaaa
; TODO parse second part of the operand
; if not calc_relative_branch_into_r14()
; return false
; cx16.r15 |= (cx16.r14 << 8)
; txt.print("TODO ZPR addrmode\n")
; txt.print("opcode=")
; txt.print_ubhex(opcode,1)
; txt.print(" op1=")
; txt.print_ubhex(lsb(cx16.r15),1)
; txt.print(" op2=")
; txt.print_ubhex(msb(cx16.r15),1)
; return false
}
ubyte num_operand_bytes = operand_size[addr_mode]
if print_emit_bytes {
txt.chrout(' ')
txt.print_uwhex(program_counter, 1)
txt.print(" ")
}
emit(opcode)
if num_operand_bytes==1 {
emit(lsb(cx16.r15))
} else if num_operand_bytes == 2 {
emit(lsb(cx16.r15))
emit(msb(cx16.r15))
}
if print_emit_bytes
txt.nl()
return true
}
txt.print("?invalid operand\n")
return false
}
txt.print("?invalid instruction\n")
return false
}
sub calc_relative_branch_into_r14() -> ubyte {
cx16.r14 = cx16.r15 - program_counter - 2
if msb(cx16.r14) {
if cx16.r14 < $ff80 {
txt.print("?branch out of range\n")
return false
}
} else if cx16.r14 > $007f {
txt.print("?branch out of range\n")
return false
}
return true
}
sub parse_operand(uword operand_ptr) -> ubyte {
; parses the operand. Returns 2 things:
; - addressing mode id as result value or 0 (am_Invalid) when error
; - operand numeric value in cx16.r15 (if applicable)
ubyte @zp firstchr = @(operand_ptr)
ubyte parsed_len
when firstchr {
0 -> return instructions.am_Imp
'#' -> {
; lda #$99 Immediate
operand_ptr++
parsed_len = conv.any2uword(operand_ptr)
if parsed_len {
operand_ptr += parsed_len
if @(operand_ptr)==0
return instructions.am_Imm
}
}
'a' -> {
if not @(operand_ptr+1)
return instructions.am_Acc ; Accumulator - no value.
; TODO its a symbol/label, immediate or indexed addressing
txt.print("TODO symbol: ")
txt.print(operand_ptr)
txt.nl()
}
'(' -> {
; various forms of indirect
operand_ptr++
parsed_len = conv.any2uword(operand_ptr)
if parsed_len {
operand_ptr+=parsed_len
if msb(cx16.r15) {
; absolute indirects
if str_is1(operand_ptr, ')')
return instructions.am_Ind
if str_is3(operand_ptr, ",x)")
return instructions.am_IaX
} else {
; zero page indirects
if str_is1(operand_ptr, ')')
return instructions.am_Izp
if str_is3(operand_ptr, ",x)")
return instructions.am_IzX
if str_is3(operand_ptr, "),y")
return instructions.am_IzY
}
}
}
'$', '%', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
; address optionally followed by ,x or ,y or ,address
parsed_len = conv.any2uword(operand_ptr)
if parsed_len {
operand_ptr += parsed_len
if msb(cx16.r15) {
; absolute or abs indirects
if @(operand_ptr)==0
return instructions.am_Abs
if str_is2(operand_ptr, ",x")
return instructions.am_AbsX
if str_is2(operand_ptr, ",y")
return instructions.am_AbsY
} else {
; zero page or zp indirects
if @(operand_ptr)==0
return instructions.am_Zp
if str_is2(operand_ptr, ",x")
return instructions.am_ZpX
if str_is2(operand_ptr, ",y")
return instructions.am_ZpY
if @(operand_ptr)==',' {
; assume BBR $zp,$aaaa or BBS $zp,$aaaa
return instructions.am_Zpr
}
}
}
}
}
return instructions.am_Invalid
}
sub process_assembler_directive(uword directive, uword operand) -> ubyte {
; we only recognise .byte right now
if string.compare(directive, ".byte")==0 {
if operand {
ubyte length
length = conv.any2uword(operand)
if length {
if msb(cx16.r15) {
txt.print("?byte value too large\n")
return false
}
if print_emit_bytes {
txt.chrout(' ')
txt.print_uwhex(program_counter, 1)
txt.print(" ")
}
emit(lsb(cx16.r15))
operand += length
while @(operand)==',' {
operand++
length = conv.any2uword(operand)
if not length
break
if msb(cx16.r15) {
txt.print("?byte value too large\n")
return false
}
emit(lsb(cx16.r15))
operand += length
}
if print_emit_bytes
txt.nl()
return true
}
}
}
txt.print("?syntax error\n")
return false
}
asmsub str_is1(uword st @R0, ubyte char @A) clobbers(Y) -> ubyte @A {
%asm {{
cmp (cx16.r0)
bne +
ldy #1
lda (cx16.r0),y
bne +
lda #1
rts
+ lda #0
rts
}}
}
asmsub str_is2(uword st @R0, uword compare @AY) clobbers(Y) -> ubyte @A {
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
jmp str_is3._is_2_entry
}}
}
asmsub str_is3(uword st @R0, uword compare @AY) clobbers(Y) -> ubyte @A {
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda (cx16.r0)
cmp (P8ZP_SCRATCH_W1)
bne +
ldy #1
_is_2_entry
lda (cx16.r0),y
cmp (P8ZP_SCRATCH_W1),y
bne +
iny
lda (cx16.r0),y
cmp (P8ZP_SCRATCH_W1),y
bne +
iny
lda (cx16.r0),y
bne +
lda #1
rts
+ lda #0
rts
}}
}
sub emit(ubyte value) {
@(program_counter) = value
program_counter++
if print_emit_bytes {
txt.print_ubhex(value, 0)
txt.chrout(' ')
}
}
sub set_symbol(uword symbolname_ptr, uword value) {
txt.print("symbol: ")
txt.print(symbolname_ptr)
txt.chrout('=')
txt.print_uwhex(value, true)
txt.nl()
}
sub dummy(uword operand_ptr) -> uword {
uword a1=rndw()
uword a6=a1+operand_ptr
return a6
}
sub split_input() {
; first strip the input string of extra whitespace and comments
ubyte copying_word = false
ubyte word_count
ubyte @zp char_idx = 0
word_addrs[0] = 0
word_addrs[1] = 0
word_addrs[2] = 0
ubyte @zp char
for char in input_line {
when char {
' ', 9, 160 -> {
if copying_word
input_line[char_idx] = 0; terminate word
copying_word = false
}
';', 0 -> {
; terminate line on comment char or end-of-string
break
}
else -> {
if not copying_word {
if word_count==3
break
word_addrs[word_count] = &input_line + char_idx
word_count++
}
copying_word = true
}
}
char_idx++
}
char = input_line[char_idx]
if char==' ' or char==9 or char==160 or char==';'
input_line[char_idx] = 0
}
sub debug_print_words() { ; TODO remove
txt.print("(debug:) words: ")
uword word_ptr
for word_ptr in word_addrs {
txt.chrout('[')
txt.print(word_ptr)
txt.print("] ")
}
txt.nl()
}
sub preprocess_assignment_spacing() {
if not string.find(input_line, '=')
return
; split the line around the '='
str input_line2 = "?" * 40
uword src = &input_line
uword dest = &input_line2
ubyte @zp cc
for cc in input_line {
if cc=='=' {
@(dest) = ' '
dest++
@(dest) = '='
dest++
cc = ' '
}
@(dest) = cc
dest++
}
@(dest)=0
void string.copy(input_line2, src)
}
}
instructions {
const ubyte am_Invalid = 0
const ubyte am_Imp = 1
const ubyte am_Acc = 2
const ubyte am_Imm = 3
const ubyte am_Zp = 4
const ubyte am_ZpX = 5
const ubyte am_ZpY = 6
const ubyte am_Rel = 7
const ubyte am_Abs = 8
const ubyte am_AbsX = 9
const ubyte am_AbsY = 10
const ubyte am_Ind = 11
const ubyte am_IzX = 12
const ubyte am_IzY = 13
const ubyte am_Zpr = 14
const ubyte am_Izp = 15
const ubyte am_IaX = 16
; TODO: explore (benchmark) hash based matchers. Faster (although the bulk of the time is not in the mnemonic matching)? Less memory?
asmsub match(uword mnemonic_ptr @AY) -> uword @AY {
; -- input: mnemonic_ptr in AY, output: pointer to instruction info structure or $0000 in AY
%asm {{
phx
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
sta cx16.r4 ; fourth letter in R4 (only exists for the few 4-letter mnemonics)
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
sta cx16.r5 ; fifth letter in R5 (should always be zero or whitespace for a valid mnemonic)
pla
tay
pla
tax
pla
jsr get_opcode_info
plx
rts
}}
}
asmsub opcode(uword instr_info_ptr @AY, ubyte addr_mode @X) clobbers(X) -> ubyte @A, ubyte @Pc {
; -- input: instruction info struct ptr @AY, desired addr_mode @X
; output: opcode @A, valid @carrybit
%asm {{
cpy #0
beq _not_found
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
stx cx16.r5
; debug result address
;sec
;jsr txt.print_uwhex
;lda #13
;jsr c64.CHROUT
ldy #0
lda (P8ZP_SCRATCH_W2),y
beq _multi_addrmodes
iny
lda (P8ZP_SCRATCH_W2),y
cmp cx16.r5 ; check single possible addr.mode
bne _not_found
iny
lda (P8ZP_SCRATCH_W2),y ; get opcode
sec
rts
_not_found lda #0
clc
rts
_multi_addrmodes
ldy cx16.r5
lda (P8ZP_SCRATCH_W2),y ; check opcode for addr.mode
bne _valid
; opcode $00 usually means 'invalid' but for "brk" it is actually valid so check for "brk"
ldy #0
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'b'
bne _not_found
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'r'
bne _not_found
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'k'
bne _not_found
lda #0
_valid sec
rts
}}
}
%asminclude "opcodes.asm", ""
}

View File

@ -1,167 +0,0 @@
%target cx16
%import test_stack
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
txt.print("\nassembler benchmark - tree match routine\n")
benchmark.benchmark()
test_stack.test()
}
}
benchmark {
sub benchmark() {
str[20] mnemonics = ["lda", "ldx", "ldy", "jsr", "bcs", "rts", "lda", "ora", "and", "eor", "wai", "nop", "wai", "nop", "wai", "nop", "wai", "nop", "wai", "nop"]
ubyte[20] modes = [3, 4, 8, 8, 7, 1, 12, 13, 5, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
uword valid = 0
const uword iterations = 40000 / len(mnemonics)
const uword amount = iterations * len(mnemonics)
txt.print("matching ")
txt.print_uw(amount)
txt.print(" mnemonics")
c64.SETTIM(0,0,0)
uword total = 0
repeat iterations {
if lsb(total)==0
txt.chrout('.')
ubyte idx
for idx in 0 to len(mnemonics)-1 {
uword instr_info = instructions.match(mnemonics[idx])
ubyte opcode = instructions.opcode(instr_info, modes[idx])
if_cs
valid++
total++
}
}
uword current_time = c64.RDTIM16()
txt.print("\nvalid: ")
txt.print_uw(valid)
txt.print("\ninvalid: ")
txt.print_uw(amount-valid)
txt.print("\ntotal: ")
txt.print_uw(total)
txt.print("\n\nseconds:")
uword secs = current_time / 60
current_time = (current_time - secs*60)*1000/60
txt.print_uw(secs)
txt.chrout('.')
if current_time<10
txt.chrout('0')
if current_time<100
txt.chrout('0')
txt.print_uw(current_time)
txt.nl()
}
}
instructions {
asmsub match(uword mnemonic_ptr @AY) -> uword @AY {
; -- input: mnemonic_ptr in AY, output: pointer to instruction info structure or $0000 in AY
%asm {{
phx
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
pha
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
sta cx16.r4 ; fourth letter in R4 (only exists for the few 4-letter mnemonics)
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
sta cx16.r5 ; fifth letter in R5 (should always be zero or whitespace for a valid mnemonic)
pla
tay
pla
tax
pla
jsr get_opcode_info
plx
rts
}}
}
asmsub opcode(uword instr_info_ptr @AY, ubyte addr_mode @X) clobbers(X) -> ubyte @A, ubyte @Pc {
; -- input: instruction info struct ptr @AY, desired addr_mode @X
; output: opcode @A, valid @carrybit
%asm {{
cpy #0
beq _not_found
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
stx cx16.r15
; debug result address
;sec
;jsr txt.print_uwhex
;lda #13
;jsr c64.CHROUT
ldy #0
lda (P8ZP_SCRATCH_W2),y
beq _multi_addrmodes
iny
lda (P8ZP_SCRATCH_W2),y
cmp cx16.r15 ; check single possible addr.mode
bne _not_found
iny
lda (P8ZP_SCRATCH_W2),y ; get opcode
sec
rts
_not_found lda #0
clc
rts
_multi_addrmodes
ldy cx16.r15
lda (P8ZP_SCRATCH_W2),y ; check opcode for addr.mode
bne _valid
; opcode $00 usually means 'invalid' but for "brk" it is actually valid so check for "brk"
ldy #0
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'b'
bne _not_found
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'r'
bne _not_found
iny
lda (P8ZP_SCRATCH_W1),y
and #$7f ; lowercase
cmp #'k'
bne _not_found
lda #0
_valid sec
rts
}}
}
%asminclude "opcodes.asm", ""
}

View File

@ -1,502 +0,0 @@
import sys
from collections import Counter
from enum import IntEnum
class AddrMode(IntEnum):
Imp = 1,
Acc = 2,
Imm = 3,
Zp = 4,
ZpX = 5,
ZpY = 6,
Rel = 7,
Abs = 8,
AbsX = 9,
AbsY = 10,
Ind = 11,
IzX = 12,
IzY = 13,
Zpr = 14,
Izp = 15,
IaX = 16
AllInstructions = [
(0x00, "brk", AddrMode.Imp),
(0x01, "ora", AddrMode.IzX),
(0x02, "nop", AddrMode.Imm),
(0x03, "nop", AddrMode.Imp),
(0x04, "tsb", AddrMode.Zp),
(0x05, "ora", AddrMode.Zp),
(0x06, "asl", AddrMode.Zp),
(0x07, "rmb0", AddrMode.Zp),
(0x08, "php", AddrMode.Imp),
(0x09, "ora", AddrMode.Imm),
(0x0a, "asl", AddrMode.Acc),
(0x0b, "nop", AddrMode.Imp),
(0x0c, "tsb", AddrMode.Abs),
(0x0d, "ora", AddrMode.Abs),
(0x0e, "asl", AddrMode.Abs),
(0x0f, "bbr0", AddrMode.Zpr),
(0x10, "bpl", AddrMode.Rel),
(0x11, "ora", AddrMode.IzY),
(0x12, "ora", AddrMode.Izp),
(0x13, "nop", AddrMode.Imp),
(0x14, "trb", AddrMode.Zp),
(0x15, "ora", AddrMode.ZpX),
(0x16, "asl", AddrMode.ZpX),
(0x17, "rmb1", AddrMode.Zp),
(0x18, "clc", AddrMode.Imp),
(0x19, "ora", AddrMode.AbsY),
(0x1a, "inc", AddrMode.Acc),
(0x1b, "nop", AddrMode.Imp),
(0x1c, "trb", AddrMode.Abs),
(0x1d, "ora", AddrMode.AbsX),
(0x1e, "asl", AddrMode.AbsX),
(0x1f, "bbr1", AddrMode.Zpr),
(0x20, "jsr", AddrMode.Abs),
(0x21, "and", AddrMode.IzX),
(0x22, "nop", AddrMode.Imm),
(0x23, "nop", AddrMode.Imp),
(0x24, "bit", AddrMode.Zp),
(0x25, "and", AddrMode.Zp),
(0x26, "rol", AddrMode.Zp),
(0x27, "rmb2", AddrMode.Zp),
(0x28, "plp", AddrMode.Imp),
(0x29, "and", AddrMode.Imm),
(0x2a, "rol", AddrMode.Acc),
(0x2b, "nop", AddrMode.Imp),
(0x2c, "bit", AddrMode.Abs),
(0x2d, "and", AddrMode.Abs),
(0x2e, "rol", AddrMode.Abs),
(0x2f, "bbr2", AddrMode.Zpr),
(0x30, "bmi", AddrMode.Rel),
(0x31, "and", AddrMode.IzY),
(0x32, "and", AddrMode.Izp),
(0x33, "nop", AddrMode.Imp),
(0x34, "bit", AddrMode.ZpX),
(0x35, "and", AddrMode.ZpX),
(0x36, "rol", AddrMode.ZpX),
(0x37, "rmb3", AddrMode.Zp),
(0x38, "sec", AddrMode.Imp),
(0x39, "and", AddrMode.AbsY),
(0x3a, "dec", AddrMode.Acc),
(0x3b, "nop", AddrMode.Imp),
(0x3c, "bit", AddrMode.AbsX),
(0x3d, "and", AddrMode.AbsX),
(0x3e, "rol", AddrMode.AbsX),
(0x3f, "bbr3", AddrMode.Zpr),
(0x40, "rti", AddrMode.Imp),
(0x41, "eor", AddrMode.IzX),
(0x42, "nop", AddrMode.Imm),
(0x43, "nop", AddrMode.Imp),
(0x44, "nop", AddrMode.Zp),
(0x45, "eor", AddrMode.Zp),
(0x46, "lsr", AddrMode.Zp),
(0x47, "rmb4", AddrMode.Zp),
(0x48, "pha", AddrMode.Imp),
(0x49, "eor", AddrMode.Imm),
(0x4a, "lsr", AddrMode.Acc),
(0x4b, "nop", AddrMode.Imp),
(0x4c, "jmp", AddrMode.Abs),
(0x4d, "eor", AddrMode.Abs),
(0x4e, "lsr", AddrMode.Abs),
(0x4f, "bbr4", AddrMode.Zpr),
(0x50, "bvc", AddrMode.Rel),
(0x51, "eor", AddrMode.IzY),
(0x52, "eor", AddrMode.Izp),
(0x53, "nop", AddrMode.Imp),
(0x54, "nop", AddrMode.ZpX),
(0x55, "eor", AddrMode.ZpX),
(0x56, "lsr", AddrMode.ZpX),
(0x57, "rmb5", AddrMode.Zp),
(0x58, "cli", AddrMode.Imp),
(0x59, "eor", AddrMode.AbsY),
(0x5a, "phy", AddrMode.Imp),
(0x5b, "nop", AddrMode.Imp),
(0x5c, "nop", AddrMode.Abs),
(0x5d, "eor", AddrMode.AbsX),
(0x5e, "lsr", AddrMode.AbsX),
(0x5f, "bbr5", AddrMode.Zpr),
(0x60, "rts", AddrMode.Imp),
(0x61, "adc", AddrMode.IzX),
(0x62, "nop", AddrMode.Imm),
(0x63, "nop", AddrMode.Imp),
(0x64, "stz", AddrMode.Zp),
(0x65, "adc", AddrMode.Zp),
(0x66, "ror", AddrMode.Zp),
(0x67, "rmb6", AddrMode.Zp),
(0x68, "pla", AddrMode.Imp),
(0x69, "adc", AddrMode.Imm),
(0x6a, "ror", AddrMode.Acc),
(0x6b, "nop", AddrMode.Imp),
(0x6c, "jmp", AddrMode.Ind),
(0x6d, "adc", AddrMode.Abs),
(0x6e, "ror", AddrMode.Abs),
(0x6f, "bbr6", AddrMode.Zpr),
(0x70, "bvs", AddrMode.Rel),
(0x71, "adc", AddrMode.IzY),
(0x72, "adc", AddrMode.Izp),
(0x73, "nop", AddrMode.Imp),
(0x74, "stz", AddrMode.ZpX),
(0x75, "adc", AddrMode.ZpX),
(0x76, "ror", AddrMode.ZpX),
(0x77, "rmb7", AddrMode.Zp),
(0x78, "sei", AddrMode.Imp),
(0x79, "adc", AddrMode.AbsY),
(0x7a, "ply", AddrMode.Imp),
(0x7b, "nop", AddrMode.Imp),
(0x7c, "jmp", AddrMode.IaX),
(0x7d, "adc", AddrMode.AbsX),
(0x7e, "ror", AddrMode.AbsX),
(0x7f, "bbr7", AddrMode.Zpr),
(0x80, "bra", AddrMode.Rel),
(0x81, "sta", AddrMode.IzX),
(0x82, "nop", AddrMode.Imm),
(0x83, "nop", AddrMode.Imp),
(0x84, "sty", AddrMode.Zp),
(0x85, "sta", AddrMode.Zp),
(0x86, "stx", AddrMode.Zp),
(0x87, "smb0", AddrMode.Zp),
(0x88, "dey", AddrMode.Imp),
(0x89, "bit", AddrMode.Imm),
(0x8a, "txa", AddrMode.Imp),
(0x8b, "nop", AddrMode.Imp),
(0x8c, "sty", AddrMode.Abs),
(0x8d, "sta", AddrMode.Abs),
(0x8e, "stx", AddrMode.Abs),
(0x8f, "bbs0", AddrMode.Zpr),
(0x90, "bcc", AddrMode.Rel),
(0x91, "sta", AddrMode.IzY),
(0x92, "sta", AddrMode.Izp),
(0x93, "nop", AddrMode.Imp),
(0x94, "sty", AddrMode.ZpX),
(0x95, "sta", AddrMode.ZpX),
(0x96, "stx", AddrMode.ZpY),
(0x97, "smb1", AddrMode.Zp),
(0x98, "tya", AddrMode.Imp),
(0x99, "sta", AddrMode.AbsY),
(0x9a, "txs", AddrMode.Imp),
(0x9b, "nop", AddrMode.Imp),
(0x9c, "stz", AddrMode.Abs),
(0x9d, "sta", AddrMode.AbsX),
(0x9e, "stz", AddrMode.AbsX),
(0x9f, "bbs1", AddrMode.Zpr),
(0xa0, "ldy", AddrMode.Imm),
(0xa1, "lda", AddrMode.IzX),
(0xa2, "ldx", AddrMode.Imm),
(0xa3, "nop", AddrMode.Imp),
(0xa4, "ldy", AddrMode.Zp),
(0xa5, "lda", AddrMode.Zp),
(0xa6, "ldx", AddrMode.Zp),
(0xa7, "smb2", AddrMode.Zp),
(0xa8, "tay", AddrMode.Imp),
(0xa9, "lda", AddrMode.Imm),
(0xaa, "tax", AddrMode.Imp),
(0xab, "nop", AddrMode.Imp),
(0xac, "ldy", AddrMode.Abs),
(0xad, "lda", AddrMode.Abs),
(0xae, "ldx", AddrMode.Abs),
(0xaf, "bbs2", AddrMode.Zpr),
(0xb0, "bcs", AddrMode.Rel),
(0xb1, "lda", AddrMode.IzY),
(0xb2, "lda", AddrMode.Izp),
(0xb3, "nop", AddrMode.Imp),
(0xb4, "ldy", AddrMode.ZpX),
(0xb5, "lda", AddrMode.ZpX),
(0xb6, "ldx", AddrMode.ZpY),
(0xb7, "smb3", AddrMode.Zp),
(0xb8, "clv", AddrMode.Imp),
(0xb9, "lda", AddrMode.AbsY),
(0xba, "tsx", AddrMode.Imp),
(0xbb, "nop", AddrMode.Imp),
(0xbc, "ldy", AddrMode.AbsX),
(0xbd, "lda", AddrMode.AbsX),
(0xbe, "ldx", AddrMode.AbsY),
(0xbf, "bbs3", AddrMode.Zpr),
(0xc0, "cpy", AddrMode.Imm),
(0xc1, "cmp", AddrMode.IzX),
(0xc2, "nop", AddrMode.Imm),
(0xc3, "nop", AddrMode.Imp),
(0xc4, "cpy", AddrMode.Zp),
(0xc5, "cmp", AddrMode.Zp),
(0xc6, "dec", AddrMode.Zp),
(0xc7, "smb4", AddrMode.Zp),
(0xc8, "iny", AddrMode.Imp),
(0xc9, "cmp", AddrMode.Imm),
(0xca, "dex", AddrMode.Imp),
(0xcb, "wai", AddrMode.Imp),
(0xcc, "cpy", AddrMode.Abs),
(0xcd, "cmp", AddrMode.Abs),
(0xce, "dec", AddrMode.Abs),
(0xcf, "bbs4", AddrMode.Zpr),
(0xd0, "bne", AddrMode.Rel),
(0xd1, "cmp", AddrMode.IzY),
(0xd2, "cmp", AddrMode.Izp),
(0xd3, "nop", AddrMode.Imp),
(0xd4, "nop", AddrMode.ZpX),
(0xd5, "cmp", AddrMode.ZpX),
(0xd6, "dec", AddrMode.ZpX),
(0xd7, "smb5", AddrMode.Zp),
(0xd8, "cld", AddrMode.Imp),
(0xd9, "cmp", AddrMode.AbsY),
(0xda, "phx", AddrMode.Imp),
(0xdb, "stp", AddrMode.Imp),
(0xdc, "nop", AddrMode.Abs),
(0xdd, "cmp", AddrMode.AbsX),
(0xde, "dec", AddrMode.AbsX),
(0xdf, "bbs5", AddrMode.Zpr),
(0xe0, "cpx", AddrMode.Imm),
(0xe1, "sbc", AddrMode.IzX),
(0xe2, "nop", AddrMode.Imm),
(0xe3, "nop", AddrMode.Imp),
(0xe4, "cpx", AddrMode.Zp),
(0xe5, "sbc", AddrMode.Zp),
(0xe6, "inc", AddrMode.Zp),
(0xe7, "smb6", AddrMode.Zp),
(0xe8, "inx", AddrMode.Imp),
(0xe9, "sbc", AddrMode.Imm),
(0xea, "nop", AddrMode.Imp),
(0xeb, "nop", AddrMode.Imp),
(0xec, "cpx", AddrMode.Abs),
(0xed, "sbc", AddrMode.Abs),
(0xee, "inc", AddrMode.Abs),
(0xef, "bbs6", AddrMode.Zpr),
(0xf0, "beq", AddrMode.Rel),
(0xf1, "sbc", AddrMode.IzY),
(0xf2, "sbc", AddrMode.Izp),
(0xf3, "nop", AddrMode.Imp),
(0xf4, "nop", AddrMode.ZpX),
(0xf5, "sbc", AddrMode.ZpX),
(0xf6, "inc", AddrMode.ZpX),
(0xf7, "smb7", AddrMode.Zp),
(0xf8, "sed", AddrMode.Imp),
(0xf9, "sbc", AddrMode.AbsY),
(0xfa, "plx", AddrMode.Imp),
(0xfb, "nop", AddrMode.Imp),
(0xfc, "nop", AddrMode.AbsX),
(0xfd, "sbc", AddrMode.AbsX),
(0xfe, "inc", AddrMode.AbsX),
(0xff, "bbs7", AddrMode.Zpr)
]
# NOP is weird, it is all over the place.
# For the 'common' immediate NOP, keep only the $EA opcode (this was the original NOP on the 6502)
Instructions = [ins for ins in AllInstructions if ins[1] != "nop"] + [(0xea, "nop", AddrMode.Imp)]
InstructionsByName = {}
for ins in Instructions:
if ins[1] not in InstructionsByName:
InstructionsByName[ins[1]] = {ins[2]: ins[0]}
else:
InstructionsByName[ins[1]][ins[2]] = ins[0]
InstructionsByMode = {}
for ins in Instructions:
if ins[2] not in InstructionsByMode:
InstructionsByMode[ins[2]] = [(ins[1], ins[0])]
else:
InstructionsByMode[ins[2]].append((ins[1], ins[0]))
def generate_mnemonics_parser():
print("; generated by opcodes.py")
print("; addressing modes:")
for mode in AddrMode:
print(";", mode.value, "=", mode.name)
print()
print("""
.enc "petscii" ;define an ascii to petscii encoding
.cdef " @", 32 ;characters
.cdef "AZ", $c1
.cdef "az", $41
.cdef "[[", $5b
.cdef "]]", $5d
.edef "<nothing>", [];replace with no bytes
""")
for instr in sorted(InstructionsByName.items()):
print("i_" + instr[0] + ":\n\t.byte ", end="")
if len(instr[1]) == 1:
# many instructions have just 1 addressing mode, save space for those
info = instr[1].popitem()
print("1,", info[0].value,",", info[1])
else:
print("0, ", end='')
mode_opcodes = []
for mode in AddrMode:
if mode in instr[1]:
mode_opcodes.append(instr[1][mode])
else:
mode_opcodes.append(0)
print(",".join(str(o) for o in mode_opcodes), end="")
print()
def determine_mnemonics():
mnemonics = list(sorted(set(ins[1] for ins in Instructions)))
# opcodes histogram (ordered by occurrence) (in kernal + basic roms of the c64):
opcode_occurrences = [
(32, 839), (133, 502), (165, 488), (0, 429), (208, 426), (169, 390), (76, 324), (240, 322), (2, 314), (160, 245),
(96, 228), (3, 201), (1, 191), (255, 186), (144, 182), (170, 175), (162, 169), (177, 165), (104, 159), (164, 158),
(132, 157), (201, 156), (72, 151), (141, 150), (200, 146), (173, 144), (166, 139), (176, 139), (16, 138),
(134, 138), (73, 127), (24, 119), (101, 113), (69, 109), (13, 107), (34, 104), (145, 103), (4, 102), (168, 101),
(221, 98), (230, 93), (48, 91), (189, 87), (41, 86), (6, 86), (9, 86), (8, 85), (79, 85), (138, 80), (10, 80),
(7, 79), (185, 77), (56, 75), (44, 75), (78, 74), (105, 73), (5, 73), (174, 73), (220, 71), (198, 69), (232, 69),
(36, 69), (202, 67), (152, 67), (95, 67), (100, 65), (102, 65), (247, 65), (188, 64), (136, 64), (84, 64),
(122, 62), (128, 61), (80, 61), (186, 60), (82, 59), (97, 58), (15, 57), (70, 57), (229, 56), (19, 55), (40, 54),
(183, 54), (65, 54), (233, 53), (180, 53), (12, 53), (171, 53), (197, 53), (83, 52), (248, 52), (112, 51),
(237, 51), (89, 50), (11, 50), (158, 50), (74, 49), (224, 48), (20, 47), (238, 47), (108, 46), (234, 46),
(251, 46), (254, 46), (184, 45), (14, 44), (163, 44), (226, 43), (211, 43), (88, 43), (98, 42), (17, 42),
(153, 42), (243, 41), (228, 41), (99, 41), (253, 41), (209, 41), (187, 39), (123, 39), (67, 39), (196, 38),
(68, 38), (35, 38), (172, 38), (175, 38), (161, 38), (85, 38), (191, 37), (113, 37), (182, 37), (151, 37),
(71, 36), (181, 35), (214, 35), (121, 35), (157, 35), (178, 35), (77, 35), (42, 34), (212, 33), (18, 33),
(127, 33), (241, 33), (21, 33), (249, 32), (23, 31), (245, 30), (142, 30), (55, 29), (140, 29), (46, 29),
(192, 29), (179, 29), (252, 29), (115, 29), (22, 29), (43, 28), (215, 28), (45, 28), (246, 28), (38, 28),
(86, 27), (225, 27), (25, 26), (239, 26), (58, 26), (167, 26), (147, 26), (217, 26), (149, 25), (30, 25),
(206, 25), (28, 24), (47, 24), (37, 24), (155, 24), (129, 23), (148, 23), (111, 23), (29, 23), (39, 23),
(51, 22), (193, 22), (236, 22), (120, 22), (64, 22), (204, 21), (210, 21), (244, 21), (52, 21), (66, 21),
(114, 20), (250, 20), (106, 20), (93, 19), (199, 19), (218, 19), (154, 19), (205, 19), (50, 19), (159, 19),
(194, 19), (49, 19), (190, 19), (103, 18), (216, 18), (213, 18), (107, 18), (131, 18), (63, 18), (94, 18),
(91, 17), (242, 17), (109, 17), (53, 16), (227, 16), (139, 16), (31, 16), (75, 16), (60, 16), (195, 15),
(231, 15), (62, 15), (59, 15), (87, 14), (207, 14), (27, 14), (90, 14), (110, 13), (223, 13), (57, 13),
(118, 12), (26, 12), (203, 12), (81, 12), (156, 12), (54, 12), (235, 12), (146, 11), (135, 11), (126, 11),
(150, 11), (130, 11), (143, 10), (61, 10), (219, 10), (124, 9), (222, 9), (125, 9), (119, 7), (137, 7),
(33, 7), (117, 5), (92, 4), (116, 3)
]
cnt = Counter()
for opcode, amount in opcode_occurrences:
cnt[AllInstructions[opcode][1]] += amount
cnt["nop"] = 13
cnt["tsb"] = 13
four_letter_mnemonics = list(sorted([ins[1] for ins in AllInstructions if len(ins[1])>3]))
for ins4 in four_letter_mnemonics:
del cnt[ins4]
cnt[ins4] = 1
mnem2 = [c[0] for c in cnt.most_common()]
if len(mnem2)!=len(mnemonics):
raise ValueError("mnem count mismatch")
return mnem2
mnemonics = determine_mnemonics()
def first_letters():
firstletters = {m[0]: 0 for m in mnemonics}
return firstletters.keys()
def second_letters(firstletter):
secondletters = {m[1]: 0 for m in mnemonics if m[0] == firstletter}
return secondletters.keys()
def third_letters(firstletter, secondletter):
thirdletters = {m[2]: 0 for m in mnemonics if m[0] == firstletter and m[1] == secondletter}
return thirdletters.keys()
def fourth_letters(firstletter, secondletter, thirdletter):
longmnem = [m for m in mnemonics if len(m) > 3]
fourthletters = {m[3]: 0 for m in longmnem if m[0] == firstletter and m[1] == secondletter and m[2] == thirdletter}
return fourthletters.keys()
def make_tree():
tree = {}
for first in first_letters():
tree[first] = {
secondletter: {
thirdletter: {
fourthletter: {}
for fourthletter in fourth_letters(first, secondletter, thirdletter)
}
for thirdletter in third_letters(first, secondletter)
}
for secondletter in second_letters(first)
}
return tree
tree = make_tree()
print("get_opcode_info .proc")
print("_mnem_fourth_letter = cx16.r4")
print("_mnem_fifth_letter = cx16.r5")
for first in tree:
print(" cmp #'%s'" % first)
print(" bne _not_%s" % first)
for second in tree[first]:
print(" cpx #'%s'" % second)
print(" bne _not_%s%s" % (first,second))
for third in tree[first][second]:
print(" cpy #'%s'" % third)
print(" bne _not_%s%s%s" % (first, second, third))
fourth = tree[first][second][third]
if fourth:
if "".join(fourth.keys()) != "01234567":
raise ValueError("fourth", fourth.keys())
print(" bra _check_%s%s%s" % (first, second, third))
else:
print(" lda _mnem_fourth_letter") # check that the fourth letter is not present
print(" bne _invalid")
print(" lda #<i_%s%s%s" % (first, second, third))
print(" ldy #>i_%s%s%s" % (first, second, third))
print(" rts")
print("_not_%s%s%s:" % (first, second, third))
print("_not_%s%s:" % (first, second))
print("_not_%s:" % first)
print("_invalid:")
print(" lda #0")
print(" ldy #0")
print(" rts")
# the 4-letter mnemonics are:
# smb[0-7]
# bbr[0-7]
# rmb[0-7]
# bbs[0-7]
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_check_%s" % fourlettermnemonic)
print(" lda #<_tab_%s" % fourlettermnemonic)
print(" ldy #>_tab_%s" % fourlettermnemonic)
print(""" sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
bra _check4""")
print("""_check4
lda _mnem_fourth_letter
cmp #'0'
bcc _invalid
cmp #'8'
bcs _invalid
lda _mnem_fifth_letter ; must have no fifth letter
bne _invalid
tay
lda (P8ZP_SCRATCH_W2),y
pha
iny
lda (P8ZP_SCRATCH_W2),y
tay
pla
rts""")
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_tab_%s" % fourlettermnemonic)
for ii in "01234567":
print(" .word i_%s%s" % (fourlettermnemonic, ii))
print(" .pend")
def generate_mnem_list():
for m in sorted(InstructionsByName):
print(m.upper())
if __name__=="__main__":
if sys.argv[1]=="--mnemlist":
generate_mnem_list()
elif sys.argv[1]=="--parser":
generate_mnemonics_parser()
else:
print("invalid arg")

View File

@ -1,17 +0,0 @@
import re
hashcode = open("perfecthash.c", "rt").read()
entries = hashcode.split("wordlist")[1].split("{")[1].split("}")[0].strip().split(",")
max_hash_value = int(re.search(r"MAX_HASH_VALUE = (\d+)", hashcode).group(1))
if len(entries) != max_hash_value+1:
raise ValueError("inconsistent number of entries parsed")
entries = [e.strip() for e in entries]
entries = [None if e.endswith('0') else e.strip('"') for e in entries]
for ix, entry in enumerate(entries):
print(ix, entry or "-")

View File

@ -1,152 +0,0 @@
def in_word_set(string: str) -> bool:
length = len(string)
wordlist = [
"PHP",
"ROR",
"STP",
"RTS",
"RTI",
"TXS",
"PHY",
"TRB",
"EOR",
"STY",
"PHX",
"TSB",
"TAY",
"STX",
"BRK",
"LSR",
"TAX",
"TSX",
"PHA",
"PLP",
"BRA",
"STA",
"ROL",
"BCS",
"SEI",
"TXA",
"LDY",
"PLY",
"INY",
"LDX",
"PLX",
"NOP",
"INX",
"CLI",
"ASL",
"SBC",
"BMI",
"LDA",
"PLA",
"ORA",
"BNE",
"ADC",
"BBS7",
"BBR7",
"CMP",
"CPY",
"INC",
"SEC",
"BCC",
"CPX",
"BPL",
"DEY",
"TYA",
"CLV",
"DEX",
"CLC",
"BBS6",
"BBR6",
"BBS5",
"BBR5",
"SMB7",
"RMB7",
"STZ",
"SED",
"BBS4",
"BBR4",
"DEC",
"BBS3",
"BBR3",
"BBS2",
"BBR2",
"AND",
"CLD",
"BBS1",
"BBR1",
"BEQ",
"SMB6",
"RMB6",
"SMB5",
"RMB5",
"BBS0",
"BBR0",
"BVS",
"WAI",
"SMB4",
"RMB4",
"JSR",
"SMB3",
"RMB3",
"SMB2",
"RMB2",
"BIT",
"SMB1",
"RMB1",
"SMB0",
"RMB0",
"BVC",
"JMP"
]
lookup = [
-1, 0, -1, 1, 2, 3, -1, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, -1, 50,
51, 52, -1, 53, 54, 55, -1, 56, 57, 58, 59, -1, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71, -1, 72, 73, 74,
75, 76, 77, 78, 79, 80, 81, 82, -1, 83, 84, 85, 86, 87,
88, 89, 90, 91, -1, -1, 92, 93, -1, -1, -1, -1, -1, 94,
95, -1, -1, -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, 97
]
asso_values = [
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 73, 66,
61, 59, 56, 49, 47, 30, 126, 126, 126, 126,
126, 126, 126, 126, 126, 10, 3, 13, 23, 9,
25, 126, 126, 1, 90, 7, 12, 22, 35, 23,
0, 28, 1, 0, 4, 4, 12, 88, 6, 4,
33, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126
]
print(len(lookup))
print(len(asso_values))
def hash(string: str, length: len) -> int:
return asso_values[ord(string[2])] + \
asso_values[ord(string[1])+1] + \
asso_values[ord(string[0])] + \
asso_values[ord(string[length - 1])]
MAX_HASH_VALUE = 125
if 3<=length<=4:
key = hash(string, length)
if key <= MAX_HASH_VALUE:
index = lookup[key]
if index>=0:
word = wordlist[index]
return word==string
return False

View File

@ -1,68 +0,0 @@
; program to test the loading speed
.cpu "65c02"
*=$0801
; 10 sys 2061
.byte $0b, $08, $0a, $00, $9e, $32, $30, $36
.byte $31, $00, $00, $00
start
phx
ldx #<_filename
ldy #>_filename
lda #10
jsr $FFBD ; SETNAM
jmp _go
_filename
.text "romdis.asm"
_go
lda #0
tax
tay
jsr $FFDB ; SETTIM 0,0,0
lda #1
ldx #8
ldy #0
jsr $FFBA ; SETLFS 1,8,0
jsr $FFC0 ; OPEN
ldx #1
jsr $FFC6 ; CHKIN, use #1 as output channel
;lda #'.'
;jsr $FFD2 ; CHROUT
; load the file ....
_loop
jsr $ffb7 ;READST
bne _eof
jsr $FFCF ;CHRIN
sta $02 ; store...
jmp _loop
_eof
; close stuff
jsr $FFCC ;CLRCHN
lda #1
jsr $FFC3 ;CLOSE
; print the time taken
jsr $FFDE ; RDTIM -> A,X,Y
tay
txa
jsr $fe03 ; GIVAYF
jsr $fe81 ; FOUT
sta 2
sty 3
ldy #0
_printlp
lda (2),y
beq _endstr
jsr $FFD2 ; CHROUT
iny
bne _printlp
_endstr
plx
rts

View File

@ -12,43 +12,48 @@ main {
txt.lowercase()
repeat {
c64.CHROUT(19) ; HOME
txt.chrout(19) ; HOME
txt.print("\n yyyy-mm-dd HH:MM:SS.jj\n\n")
void cx16.clock_get_date_time()
c64.CHROUT(' ')
txt.chrout(' ')
print_date()
c64.CHROUT(' ')
txt.chrout(' ')
print_time()
}
txt.chrout('\n')
txt.chrout('\n')
uword jiffies = c64.RDTIM16()
txt.print_uw(jiffies)
}
}
sub print_date() {
txt.print_uw(1900 + lsb(cx16.r0))
c64.CHROUT('-')
txt.chrout('-')
if msb(cx16.r0) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(msb(cx16.r0))
c64.CHROUT('-')
txt.chrout('-')
if lsb(cx16.r1) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(lsb(cx16.r1))
}
sub print_time() {
if msb(cx16.r1) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(msb(cx16.r1))
c64.CHROUT(':')
txt.chrout(':')
if lsb(cx16.r2) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(lsb(cx16.r2))
c64.CHROUT(':')
txt.chrout(':')
if msb(cx16.r2) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(msb(cx16.r2))
c64.CHROUT('.')
txt.chrout('.')
if lsb(cx16.r3) < 10
c64.CHROUT('0')
txt.chrout('0')
txt.print_ub(lsb(cx16.r3))
}
}

View File

@ -1,109 +0,0 @@
%target cx16
%import gfx2
%import diskio
bmp_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
ubyte[$36] header
uword size
uword width
uword height
ubyte bpp
uword offsetx
uword offsety
uword palette = memory("palette", 256*4)
uword total_read = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(header, $36)
if size==$36 {
total_read = $36
if header[0]=='b' and header[1]=='m' {
uword bm_data_offset = mkword(header[11], header[10])
uword header_size = mkword(header[$f], header[$e])
width = mkword(header[$13], header[$12])
height = mkword(header[$17], header[$16])
bpp = header[$1c]
uword num_colors = header[$2e]
if num_colors == 0
num_colors = 2**bpp
uword skip_hdr = header_size - 40
repeat skip_hdr
void c64.CHRIN()
total_read += skip_hdr
size = diskio.f_read(palette, num_colors*4)
if size==num_colors*4 {
total_read += size
repeat bm_data_offset - total_read
void c64.CHRIN()
gfx2.clear_screen()
custompalette.set_bgra(palette, num_colors)
decode_bitmap()
load_ok = true
}
}
}
diskio.f_close()
}
return load_ok
sub start_plot() {
offsetx = 0
offsety = 0
if width < gfx2.width
offsetx = (gfx2.width - width - 1) / 2
if height < gfx2.height
offsety = (gfx2.height - height - 1) / 2
if width > gfx2.width
width = gfx2.width
if height > gfx2.height
height = gfx2.height
}
sub decode_bitmap() {
start_plot()
uword bits_width = width * bpp
ubyte pad_bytes = (((bits_width + 31) >> 5) << 2) - ((bits_width + 7) >> 3) as ubyte
uword x
uword y
ubyte b
for y in height-1 downto 0 {
gfx2.position(offsetx, offsety+y)
when bpp {
8 -> {
for x in 0 to width-1
gfx2.next_pixel(c64.CHRIN())
}
4 -> {
for x in 0 to width-1 step 2 {
b = c64.CHRIN()
gfx2.next_pixel(b>>4)
gfx2.next_pixel(b&15)
}
}
2 -> {
for x in 0 to width-1 step 4 {
b = c64.CHRIN()
gfx2.next_pixel(b>>6)
gfx2.next_pixel(b>>4 & 3)
gfx2.next_pixel(b>>2 & 3)
gfx2.next_pixel(b & 3)
}
}
1 -> {
for x in 0 to width-1 step 8
gfx2.set_8_pixels_from_bits(c64.CHRIN(), 1, 0)
}
}
repeat pad_bytes
void c64.CHRIN()
}
}
}
}

View File

@ -1,132 +0,0 @@
%target cx16
%import gfx2
%import textio
%import diskio
%option no_sysinit
; CommanderX16 Image file format. (EXPERIMENTAL/ UNFINISHED)
; Numbers are encoded in little endian format (lsb first).
;
; offset value
; -----------------
; HEADER (12 bytes):
; 0-1 'CI' in petscii , from "CommanderX16 Image".
; 2 Size of the header data following this byte (always 9, could become more if format changes)
; 3-4 Width in pixels (must be multiple of 8)
; 5-6 Height in pixels
; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors)
; this also determines the number of palette entries following later.
; 8 Settings bits.
; bit 0 and 1 = compression. 00 = uncompressed
; 01 = RLE [TODO not yet implemented]
; 10 = LZSA [TODO not yet implemented]
; 11 = Exomizer [TODO not yet implemented]
; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB)
; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB)
; 4 bits per channel is what the Vera in the Cx16 supports.
; bit 3 = bitmap format. 0 = raw bitmap pixels
; 1 = tile-based image [TODO not yet implemented]
; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels
; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels
; bit 6,7: reserved, set to 0
; 9-11 Size of the bitmap data following the palette data.
; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end).
;
; PALETTE (always present but size varies):
; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per
; entry is 2 or 3, depending on the chosen palette format in the setting bits.
;
; BITMAPDATA (size varies):
; After this, the actual image data follows.
; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence
; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8
; If it is 'tiles', .... [TODO]
; If a compression scheme is used, the bitmap data here has to be decompressed first.
; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed?
; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that)
; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen)
ci_module {
sub show_image(uword filename) -> ubyte {
uword buffer = memory("buffer", 620*2) ; enough to store two scanlines of 640 bytes
ubyte read_success = false
uword bitmap_load_address = sys.progend()
; uword max_bitmap_size = $9eff - bitmap_load_address
if(diskio.f_open(8, filename)) {
uword size = diskio.f_read(buffer, 12) ; read the header
if size==12 {
if @(buffer+0)=='c' and @(buffer+1)=='i' and @(buffer+2) == 9 {
uword width = mkword(@(buffer+4), @(buffer+3))
uword height = mkword(@(buffer+6), @(buffer+5))
ubyte bpp = @(buffer+7)
uword num_colors = 2 ** bpp
ubyte flags = @(buffer+8)
ubyte compression = flags & %00000011
ubyte palette_format = (flags & %00000100) >> 2
ubyte bitmap_format = (flags & %00001000) >> 3
; ubyte hscale = (flags & %00010000) >> 4
; ubyte vscale = (flags & %00100000) >> 5
uword bitmap_size = mkword(@(buffer+10), @(buffer+9))
uword palette_size = num_colors*2
if palette_format
palette_size += num_colors ; 3
if width > gfx2.width {
txt.print("image is too wide for the display!\n")
} else if compression!=0 {
txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions
} else if bitmap_format==1 {
txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image
} else {
size = diskio.f_read(buffer, palette_size)
if size==palette_size {
if compression {
txt.print("todo: compressed image support\n")
} else {
; uncompressed bitmap data. read it a scanline at a time and display as we go.
; restrict height to the maximun that can be displayed
if height > gfx2.height
height = gfx2.height
if palette_format
palette.set_rgb8(buffer, num_colors)
else
palette.set_rgb4(buffer, num_colors)
gfx2.clear_screen()
gfx2.position(0 ,0)
uword scanline_size = width * bpp / 8
ubyte y
for y in 0 to lsb(height)-1 {
void diskio.f_read(buffer, scanline_size)
when bpp {
8 -> gfx2.next_pixels(buffer, scanline_size)
4 -> display_scanline_16c(buffer, scanline_size)
2 -> display_scanline_4c(buffer, scanline_size)
1 -> display_scanline_2c(buffer, scanline_size)
}
}
read_success = true
}
}
}
}
}
diskio.f_close()
}
return read_success
}
sub display_scanline_16c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_4c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_2c(uword dataptr, uword numbytes) {
; TODO
}
}

View File

@ -1,362 +0,0 @@
%target cx16
%import gfx2
%import textio
%import diskio
iff_module {
uword cmap
uword num_colors
uword[16] cycle_rates
uword[16] cycle_rate_ticks
ubyte[16] cycle_reverseflags
ubyte[16] cycle_lows
ubyte[16] cycle_highs
ubyte num_cycles
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
uword size
ubyte[32] buffer
uword camg = 0
str chunk_id = "????"
uword chunk_size_hi
uword chunk_size_lo
uword scanline_data_ptr = sys.progend()
uword width
uword height
ubyte num_planes
ubyte compression
ubyte have_cmap = false
ubyte cycle_crng = false
ubyte cycle_ccrt = false
num_cycles = 0
cmap = memory("palette", 256*4) ; only use 768 of these, but this allows re-use of the same block that the bmp module allocates
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(buffer, 12)
if size==12 {
if buffer[0]=='f' and buffer[1]=='o' and buffer[2]=='r' and buffer[3]=='m'
and buffer[8]=='i' and buffer[9]=='l' and buffer[10]=='b' and buffer[11]=='m'{
while read_chunk_header() {
if chunk_id == "bmhd" {
void diskio.f_read(buffer, chunk_size_lo)
width = mkword(buffer[0], buffer[1])
height = mkword(buffer[2], buffer[3])
num_planes = buffer[8]
num_colors = 2 ** num_planes
compression = buffer[10]
}
else if chunk_id == "camg" {
void diskio.f_read(buffer, chunk_size_lo)
camg = mkword(buffer[2], buffer[3])
if camg & $0800 {
txt.print("ham mode not supported!\n")
break
}
}
else if chunk_id == "cmap" {
have_cmap = true
void diskio.f_read(cmap, chunk_size_lo)
}
else if chunk_id == "crng" {
; DeluxePaint color cycle range
if not cycle_ccrt {
cycle_crng = true
void diskio.f_read(buffer, chunk_size_lo)
ubyte flags = buffer[5]
if flags & 1 {
cycle_rates[num_cycles] = mkword(buffer[2], buffer[3])
cycle_rate_ticks[num_cycles] = 1
cycle_lows[num_cycles] = buffer[6]
cycle_highs[num_cycles] = buffer[7]
cycle_reverseflags[num_cycles] = flags & 2 != 0
num_cycles++
}
} else
skip_chunk()
}
else if chunk_id == "ccrt" {
; Graphicraft color cycle range
if not cycle_crng {
cycle_ccrt = true
void diskio.f_read(buffer, chunk_size_lo)
ubyte direction = buffer[1]
if direction {
; delay_sec = buffer[4] * 256 * 256 * 256 + buffer[5] * 256 * 256 + buffer[6] * 256 + buffer[7]
; delay_micro = buffer[8] * 256 * 256 * 256 + buffer[9] * 256 * 256 + buffer[10] * 256 + buffer[11]
; We're ignoring the delay_sec field for now. Not many images will have this slow of a color cycle anyway (>1 sec per cycle)
; rate = int(16384 // (60*delay_micro/1e6))
; float rate = (65*16384.0) / (mkword(buffer[9], buffer[10]) as float) ; fairly good approximation using float arithmetic
cycle_rates[num_cycles] = 33280 / (mkword(buffer[9], buffer[10]) >> 5) ; reasonable approximation using only 16-bit integer arithmetic
cycle_rate_ticks[num_cycles] = 1
cycle_lows[num_cycles] = buffer[2]
cycle_highs[num_cycles] = buffer[3]
cycle_reverseflags[num_cycles] = direction == 1 ; TODO weird, the spec say that -1 = reversed but several example images that I have downloaded are the opposite
num_cycles++
}
} else
skip_chunk()
}
else if chunk_id == "body" {
gfx2.clear_screen()
if camg & $0004
height /= 2 ; interlaced: just skip every odd scanline later
if camg & $0080 and have_cmap
make_ehb_palette()
palette.set_rgb8(cmap, num_colors)
if compression
decode_rle()
else
decode_raw()
load_ok = true
break ; done after body
}
else {
skip_chunk()
}
}
} else
txt.print("not an iff ilbm file!\n")
}
diskio.f_close()
}
return load_ok
sub read_chunk_header() -> ubyte {
size = diskio.f_read(buffer, 8)
if size==8 {
chunk_id[0] = buffer[0]
chunk_id[1] = buffer[1]
chunk_id[2] = buffer[2]
chunk_id[3] = buffer[3]
chunk_size_hi = mkword(buffer[4], buffer[5])
chunk_size_lo = mkword(buffer[6], buffer[7])
return true
}
return false
}
sub skip_chunk() {
repeat lsb(chunk_size_hi)*8 + (chunk_size_lo >> 13)
void diskio.f_read(scanline_data_ptr, $2000)
void diskio.f_read(scanline_data_ptr, chunk_size_lo & $1fff)
}
sub make_ehb_palette() {
; generate 32 additional Extra-Halfbrite colors in the cmap
uword palletteptr = cmap
uword ehbptr = palletteptr + 32*3
repeat 32 {
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
}
}
ubyte bitplane_stride
uword interleave_stride
uword offsetx
uword offsety
sub start_plot() {
bitplane_stride = lsb(width>>3)
interleave_stride = (bitplane_stride as uword) * num_planes
offsetx = 0
offsety = 0
if width < gfx2.width
offsetx = (gfx2.width - width - 1) / 2
if height < gfx2.height
offsety = (gfx2.height - height - 1) / 2
if width > gfx2.width
width = gfx2.width
if height > gfx2.height
height = gfx2.height
}
sub decode_raw() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
void diskio.f_read(scanline_data_ptr, interleave_stride)
if interlaced
void diskio.f_read(scanline_data_ptr, interleave_stride)
gfx2.position(offsetx, offsety+y)
planar_to_chunky_scanline()
}
}
sub decode_rle() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
decode_rle_scanline()
if interlaced
decode_rle_scanline()
gfx2.position(offsetx, offsety+y)
planar_to_chunky_scanline()
}
}
sub decode_rle_scanline() {
uword x = interleave_stride
uword plane_ptr = scanline_data_ptr
while x {
ubyte b = c64.CHRIN()
if b > 128 {
ubyte b2 = c64.CHRIN()
repeat 2+(b^255) {
@(plane_ptr) = b2
plane_ptr++
x--
}
} else if b < 128 {
repeat b+1 {
@(plane_ptr) = c64.CHRIN()
plane_ptr++
x--
}
} else
break
}
}
sub planar_to_chunky_scanline() {
; ubyte[8] masks = [128,64,32,16,8,4,2,1]
uword x
for x in 0 to width-1 {
; ubyte mask = masks[lsb(x) & 7]
uword pixptr = x/8 + scanline_data_ptr
ubyte bits = 0
%asm {{
bra +
_masks .byte 128, 64, 32, 16, 8, 4, 2, 1
+ lda pixptr
sta P8ZP_SCRATCH_W1
lda pixptr+1
sta P8ZP_SCRATCH_W1+1
lda x
and #7
tay
lda _masks,y
sta P8ZP_SCRATCH_B1 ; mask
phx
ldx num_planes
ldy #0
- lda (P8ZP_SCRATCH_W1),y
clc
and P8ZP_SCRATCH_B1
beq +
sec
+ ror bits ; shift planar bit into chunky byte
lda P8ZP_SCRATCH_W1
; clc
adc bitplane_stride
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ dex
bne -
plx
lda #8
sec
sbc num_planes
beq +
tay
- lsr bits
dey
bne -
+
}}
; the assembly above is the optimized version of this:
; repeat num_planes {
; clear_carry()
; if @(pixptr) & mask
; set_carry()
; ror(bits) ; shift planar bit into chunky byte
; pixptr += bitplane_stride
; }
; bits >>= 8-num_planes
gfx2.next_pixel(bits)
}
}
}
sub cycle_colors_each_jiffy() {
if num_cycles==0
return
; TODO implement Blend Shifting see http://www.effectgames.com/demos/canvascycle/palette.js
ubyte changed = false
ubyte ci
for ci in 0 to num_cycles-1 {
cycle_rate_ticks[ci]--
if cycle_rate_ticks[ci]==0 {
changed = true
cycle_rate_ticks[ci] = 16384 / cycle_rates[ci]
do_cycle(cycle_lows[ci], cycle_highs[ci], cycle_reverseflags[ci])
}
}
if changed
palette.set_rgb8(cmap, num_colors) ; set the new palette
sub do_cycle(uword low, uword high, ubyte reversed) {
low *= 3
high *= 3
uword bytecount = high-low
uword cptr
ubyte red
ubyte green
ubyte blue
if reversed {
cptr = cmap + low
red = @(cptr)
green = @(cptr+1)
blue = @(cptr+2)
repeat bytecount {
@(cptr) = @(cptr+3)
cptr++
}
@(cptr) = red
cptr++
@(cptr) = green
cptr++
@(cptr) = blue
} else {
cptr = cmap + high
red = @(cptr)
cptr++
green = @(cptr)
cptr++
blue = @(cptr)
repeat bytecount {
@(cptr) = @(cptr-3)
cptr--
}
@(cptr) = blue
cptr--
@(cptr) = green
cptr--
@(cptr) = red
}
}
}
}

View File

@ -1,169 +0,0 @@
%target cx16
%import gfx2
%import textio
%import diskio
%import string
%import koala_module
%import iff_module
%import pcx_module
%import bmp_module
;; %import ci_module
%zeropage basicsafe
main {
sub start() {
; trick to check if we're running on sdcard or host system shared folder
txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n")
if string.length(diskio.status(8)) {
txt.print("enter image file name or just enter for all on disk: ")
ubyte i = txt.input_chars(diskio.filename)
gfx2.screen_mode(1) ; 320*240, 256c
if i
attempt_load(diskio.filename)
else
show_pics_sdcard()
; txt.print("\nnothing more to do.\n")
}
else
txt.print("files are read with sequential file loading.\nin the emulator this currently only works with files on an sd-card image.\nsorry :(\n")
gfx2.screen_mode(255) ; back to default text mode and palette
txt.print("that was all folks!\n")
}
sub show_pics_sdcard() {
; load and show the images on the disk with the given extensions.
; this only works in the emulator V38 with an sd-card image with the files on it.
str[40] filename_ptrs
ubyte num_files = diskio.list_files(8, 0, &filename_ptrs, len(filename_ptrs))
if num_files {
while num_files {
num_files--
attempt_load(filename_ptrs[num_files])
}
} else
txt.print("no files in directory!\n")
}
sub attempt_load(uword filenameptr) {
;txt.print(">> ")
;txt.print(filenameptr)
;txt.nl()
uword extension = filenameptr + rfind(filenameptr, '.')
if string.compare(extension, ".iff")==0 {
;txt.print("loading ")
;txt.print("iff\n")
if iff_module.show_image(filenameptr) {
if iff_module.num_cycles {
repeat 500 {
sys.wait(1)
iff_module.cycle_colors_each_jiffy()
}
}
else
sys.wait(180)
} else {
load_error(filenameptr)
}
}
else if string.compare(extension, ".pcx")==0 {
;txt.print("loading ")
;txt.print("pcx\n")
if pcx_module.show_image(filenameptr) {
sys.wait(180)
} else {
load_error(filenameptr)
}
}
else if string.compare(extension,".koa")==0 {
;txt.print("loading ")
;txt.print("koala\n")
if koala_module.show_image(filenameptr) {
sys.wait(180)
} else {
load_error(filenameptr)
}
}
else if string.compare(extension, ".bmp")==0 {
;txt.print("loading ")
;txt.print("bmp\n")
if bmp_module.show_image(filenameptr) {
sys.wait(180)
} else {
load_error(filenameptr)
}
}
; else if extension == ".ci" {
;; txt.print("loading ")
;; txt.print("ci\n")
; if ci_module.show_image(filenameptr) {
; sys.wait(180)
; } else {
; load_error(filenameptr)
; }
; }
}
sub load_error(uword filenameptr) {
gfx2.screen_mode(255) ; back to default text mode and palette
txt.print(filenameptr)
txt.print(": load error\n")
sys.exit(1)
}
sub extension_equals(uword stringptr, uword extensionptr) -> ubyte {
ubyte ix = rfind(stringptr, '.')
return ix<255 and string.compare(stringptr+ix, extensionptr)==0
}
sub rfind(uword stringptr, ubyte char) -> ubyte {
ubyte i
for i in string.length(stringptr)-1 downto 0 {
if @(stringptr+i)==char
return i
}
return 0
}
}
custompalette {
sub set_bgra(uword palletteptr, uword num_colors) {
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
; 4 bytes per color entry (BGRA), adjust color depth from 8 to 4 bits per channel.
repeat num_colors {
red = @(palletteptr+2) >> 4
greenblue = @(palletteptr+1) & %11110000
greenblue |= @(palletteptr+0) >> 4 ; add Blue
palletteptr+=4
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_grayscale256() {
; grays $000- $fff stretched out over all the 256 colors
ubyte c = 0
uword vera_palette_ptr = $fa00
repeat 16 {
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
}
c += $11
}
}
}

View File

@ -1,84 +0,0 @@
%target cx16
%import gfx2
%import diskio
%import palette
koala_module {
const uword load_location = $6000
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok=false
if diskio.f_open(8, filenameptr) {
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
if size==2 {
if diskio.f_read(load_location, 10001)==10001 {
; set a better C64 color palette, the Cx16's default is too saturated
palette.set_c64pepto()
convert_koalapic()
load_ok = true
}
}
diskio.f_close()
}
return load_ok
}
sub convert_koalapic() {
ubyte cy
ubyte @zp cx
uword @zp cy_times_forty = 0
ubyte @zp d
uword bitmap_ptr = load_location
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
ubyte[8] pixels
gfx2.clear_screen()
uword offsety = (gfx2.height - 200) / 2
for cy in 0 to 24*8 step 8 {
uword posy = cy + offsety
for cx in 0 to 39 {
uword posx = cx as uword * 8
for d in 0 to 7 {
gfx2.position(posx, posy + d)
get_8_pixels()
gfx2.next_pixels(pixels, 8)
}
}
cy_times_forty += 40
}
sub get_8_pixels() {
ubyte bm = @(bitmap_ptr)
ubyte @zp m = mcol(bm)
pixels[7] = m
pixels[6] = m
bm >>= 2
m = mcol(bm)
pixels[5] = m
pixels[4] = m
bm >>= 2
m = mcol(bm)
pixels[3] = m
pixels[2] = m
bm >>= 2
m = mcol(bm)
pixels[1] = m
pixels[0] = m
bitmap_ptr++
sub mcol(ubyte b) -> ubyte {
ubyte @zp color
when b & 3 {
0 -> color = @(load_location + 8000 + 1000 + 1000) & 15
1 -> color = @(load_location + 8000 + cy_times_forty + cx) >>4
2 -> color = @(load_location + 8000 + cy_times_forty + cx) & 15
else -> color = @(load_location + 8000 + 1000 + cy_times_forty + cx) & 15
}
return color
}
}
}
}

View File

@ -1,170 +0,0 @@
%target cx16
%import gfx2
%import textio
%import diskio
pcx_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
if diskio.f_open(8, filenameptr) {
ubyte[128] header
uword size = diskio.f_read(header, 128)
if size==128 {
if header[0] == $0a and header[2] == 1 {
ubyte bits_per_pixel = header[3]
if bits_per_pixel==1 or bits_per_pixel==4 or bits_per_pixel==8 {
uword width = mkword(header[$09], header[$08]) - mkword(header[$05], header[$04]) + 1
uword height = mkword(header[$0b], header[$0a]) - mkword(header[$07], header[$06]) + 1
ubyte number_of_planes = header[$41]
uword palette_format = mkword(header[$45], header[$44])
uword num_colors = 2**bits_per_pixel
if number_of_planes == 1 {
if (width & 7) == 0 {
gfx2.clear_screen()
if palette_format==2
custompalette.set_grayscale256()
else if num_colors == 16
palette.set_rgb8(&header + $10, 16)
else if num_colors == 2
palette.set_monochrome()
when bits_per_pixel {
8 -> load_ok = bitmap.do8bpp(width, height)
4 -> load_ok = bitmap.do4bpp(width, height)
1 -> load_ok = bitmap.do1bpp(width, height)
}
if load_ok {
load_ok = c64.CHRIN()
if load_ok == 12 {
; there is 256 colors of palette data at the end
uword palette_mem = sys.progend()
load_ok = false
size = diskio.f_read(palette_mem, 3*256)
if size==3*256 {
load_ok = true
palette.set_rgb8(palette_mem, num_colors)
}
}
}
} else
txt.print("width not multiple of 8!\n")
} else
txt.print("has >256 colors!\n")
}
}
}
diskio.f_close()
}
return load_ok
}
}
bitmap {
uword offsetx
uword offsety
uword py
uword px
ubyte y_ok
ubyte status
sub start_plot(uword width, uword height) {
offsetx = 0
offsety = 0
y_ok = true
py = 0
px = 0
if width < gfx2.width
offsetx = (gfx2.width - width) / 2
if height < gfx2.height
offsety = (gfx2.height - height) / 2
status = (not c64.READST()) or (c64.READST()&64==64)
}
sub next_scanline() {
px = 0
py++
y_ok = py < gfx2.height
gfx2.position(offsetx, offsety+py)
status = (not c64.READST()) or (c64.READST()&64==64)
}
sub do1bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
gfx2.position(offsetx, offsety)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
repeat b {
if y_ok
gfx2.set_8_pixels_from_bits(dat, 1, 0)
px += 8
}
} else {
if y_ok
gfx2.set_8_pixels_from_bits(b, 1, 0)
px += 8
}
if px==width
next_scanline()
}
return status
}
sub do4bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
gfx2.position(offsetx, offsety)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b {
gfx2.next_pixel(dat>>4)
gfx2.next_pixel(dat & 15)
}
px += b*2
} else {
if y_ok {
gfx2.next_pixel(b>>4)
gfx2.next_pixel(b & 15)
}
px += 2
}
if px==width
next_scanline()
}
return status
}
sub do8bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
gfx2.position(offsetx, offsety)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b
gfx2.next_pixel(dat)
px += b
} else {
if y_ok
gfx2.next_pixel(b)
px++
}
if px==width
next_scanline()
}
return status
}
}

View File

@ -34,7 +34,7 @@ main {
iter++
}
txt.color2(1, max_iter-iter)
txt.chrout(' ')
txt.spc()
}
txt.nl()
}

View File

@ -79,7 +79,7 @@ main {
for x in 39 downto 0 {
; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location
ubyte cc = xbuf[x] + ybuf[y]
@(screen) = cc
@(screen+x) = cc
; this is the fastest way to do this inner part:
; %asm {{
; ldy i
@ -90,8 +90,8 @@ main {
; ldy #0
; sta (screen),y
; }}
screen++
}
}
screen += 40
}
}

View File

@ -1,20 +1,10 @@
%import textio
%import string
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
txt.print_uwhex(memory("a", 100), 1)
txt.nl()
txt.print_uwhex(memory("a", 200), 1)
txt.nl()
txt.print_uwhex(memory("a", 200), 1)
txt.nl()
txt.print_uwhex(memory("b", 200), 1)
txt.nl()
txt.print("hello\n")
}
}

View File

@ -1,141 +0,0 @@
%import textio
%import floats
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
; this is only a parser/compiler test, there's no actual working program
sub start() {
txt.print("this is only a parser/compiler test\n")
return
str s1 = "hello"
str s2 = @"screencodes"
&str ms1 = $c000
byte[4] barray
ubyte[4] ubarray
word[4] warray
uword[4] uwarray
float[4] flarray
&byte[4] mbarray = $c000
&ubyte[4] mubarray = $c000
&word[4] mwarray = $c000
&uword[4] muwarray = $c000
&float[4] mflarray = $c000
ubyte a
byte bb
ubyte ub
word ww
uword uw
float fl
; read array
a=s1[2]
ub=s1[2]
bb=barray[2]
ub=ubarray[2]
ww=warray[2]
uw=uwarray[2]
fl=flarray[2]
a=ms1[2]
ub=ms1[2]
bb=mbarray[2]
ub=mubarray[2]
ww=mwarray[2]
uw=muwarray[2]
fl=mflarray[2]
a=s1[a]
ub=s1[a]
bb=barray[a]
ub=ubarray[a]
ww=warray[a]
uw=uwarray[a]
fl=flarray[a]
a=ms1[a]
ub=ms1[a]
bb=mbarray[a]
ub=mubarray[a]
ww=mwarray[a]
uw=muwarray[a]
fl=mflarray[a]
a=s1[bb]
ub=s1[bb]
bb=barray[bb]
ub=ubarray[bb]
ww=warray[bb]
uw=uwarray[bb]
fl=flarray[bb]
a=ms1[bb]
ub=ms1[bb]
bb=mbarray[bb]
ub=mubarray[bb]
ww=mwarray[bb]
uw=muwarray[bb]
fl=mflarray[bb]
; a=s1[bb*3]
; ub=s1[bb*3]
; bb=barray[bb*3]
; ub=ubarray[bb*3]
; ww=warray[bb*3]
; uw=uwarray[bb*3]
; fl=flarray[bb*3]
; a=ms1[bb*3]
; ub=ms1[bb*3]
; bb=mbarray[bb*3]
; ub=mubarray[bb*3]
; ww=mwarray[bb*3]
; uw=muwarray[bb*3]
; fl=mflarray[bb*3]
; write array
barray[2]++
barray[2]--
s1[2] = a
s1[2] = ub
barray[2] = bb
ubarray[2] = ub
warray[2] = ww
uwarray[2] = uw
flarray[2] = fl
ms1[2] = a
ms1[2] = ub
mbarray[2]++
mbarray[2] = bb
mbarray[2] = bb
mubarray[2] = ub
mwarray[2] = ww
muwarray[2] = uw
mflarray[2] = fl
s1[a] = ub
barray[a] = bb
ubarray[a] = ub
warray[a] = ww
uwarray[a] = uw
flarray[a] = fl
s1[bb] = ub
barray[bb] = bb
ubarray[bb] = ub
warray[bb] = ww
uwarray[bb] = uw
flarray[bb] = fl
; s1[bb*3] = ub
; barray[bb*3] = bb
; ubarray[bb*3] = ub
; warray[bb*3] = ww
; uwarray[bb*3] = uw
; flarray[bb*3] = fl
}
}

View File

@ -472,7 +472,7 @@ galaxy {
txt.chrout('*')
else
txt.chrout('-')
txt.chrout(' ')
txt.spc()
planet.name = make_current_planet_name()
planet.display(true)
txt.print(" (")
@ -843,9 +843,9 @@ planet {
print_name_uppercase()
txt.print(" TL:")
txt.print_ub(techlevel+1)
txt.chrout(' ')
txt.spc()
txt.print(econnames[economy])
txt.chrout(' ')
txt.spc()
txt.print(govnames[govtype])
} else {
txt.print("\n\nSystem: ")
@ -854,7 +854,7 @@ planet {
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.chrout(' ')
txt.spc()
txt.chrout('#')
txt.print_ub(number)
txt.print("\nEconomy: ")
@ -873,15 +873,15 @@ planet {
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.chrout(' ')
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.chrout(' ')
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.chrout(' ')
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
@ -946,7 +946,7 @@ util {
sub print_right(ubyte width, uword s) {
repeat width - string.length(s) {
txt.chrout(' ')
txt.spc()
}
txt.print(s)
}

View File

@ -1,5 +1,5 @@
#!/usr/bin/env sh
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out
rm -rf build out

View File

@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;offsetof;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
</highlighting>
<extensionMap>
<mapping ext="p8" />