mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
9d4ec4a9b2 | |||
cdc6d9aa65 | |||
997bc21feb | |||
975af4764d | |||
bf69219f98 | |||
f34f9329f1 | |||
90271d0dcd | |||
195cd7597d | |||
4a81406262 | |||
f9fd426843 | |||
e612056ecd | |||
6f0103398b | |||
afb60db382 | |||
5731b876ff | |||
055f917a2e | |||
4ed7fb771c | |||
c328e9018c | |||
b270f6f713 | |||
5c13918f11 | |||
40cc216557 | |||
1481f92cb0 | |||
76d54fbe5c | |||
9f72779cdc | |||
3dcef89a74 | |||
46373717b6 | |||
7277c08fa6 | |||
04e75455c4 | |||
8ac17ae14e |
@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}}
|
||||
}
|
||||
|
@ -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 {{
|
||||
|
@ -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()
|
||||
|
@ -1 +1 @@
|
||||
6.0-BETA
|
||||
6.0
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
^^^^^^^^^
|
||||
|
@ -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
|
||||
|
@ -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")
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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", ""
|
||||
}
|
@ -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", ""
|
||||
}
|
@ -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")
|
@ -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 "-")
|
@ -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
|
@ -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
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ main {
|
||||
iter++
|
||||
}
|
||||
txt.color2(1, max_iter-iter)
|
||||
txt.chrout(' ')
|
||||
txt.spc()
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
<keywords keywords="&;->;@;\$;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" />
|
||||
|
Reference in New Issue
Block a user