mirror of
https://github.com/irmen/prog8.git
synced 2024-11-02 22:04:40 +00:00
refactoring symbol lookups
This commit is contained in:
parent
ac5f45d2d4
commit
08d2f8568b
@ -456,7 +456,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetIdentifier = assignTarget.identifier
|
val targetIdentifier = assignTarget.identifier
|
||||||
if (targetIdentifier != null) {
|
if (targetIdentifier != null) {
|
||||||
val targetName = targetIdentifier.nameInSource
|
val targetName = targetIdentifier.nameInSource
|
||||||
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
|
when (val targetSymbol = program.namespace.lookup(targetName, assignment.definingScope)) {
|
||||||
null -> {
|
null -> {
|
||||||
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
||||||
return
|
return
|
||||||
@ -1073,7 +1073,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||||
if(postIncrDecr.target.identifier != null) {
|
if(postIncrDecr.target.identifier != null) {
|
||||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||||
val target = program.namespace.lookup(targetName, postIncrDecr)
|
val target = program.namespace.lookup(targetName, postIncrDecr.definingScope)
|
||||||
if(target==null) {
|
if(target==null) {
|
||||||
val symbol = postIncrDecr.target.identifier!!
|
val symbol = postIncrDecr.target.identifier!!
|
||||||
errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||||
|
@ -42,7 +42,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
if(decl.name in compTarget.machine.opcodeNames)
|
if(decl.name in compTarget.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
val existing = program.namespace.lookup(listOf(decl.name), decl.definingScope)
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
@ -75,7 +75,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
||||||
val paramsToCheck = paramNames.intersect(namesInSub)
|
val paramsToCheck = paramNames.intersect(namesInSub)
|
||||||
for(name in paramsToCheck) {
|
for(name in paramsToCheck) {
|
||||||
val labelOrVar = subroutine.searchLabelOrVariableNotSubscoped(name)
|
// TODO clean this up? no two separate lookups?
|
||||||
|
val labelOrVar = subroutine.searchLabelOrVariableNotSubscoped(name, false)
|
||||||
if(labelOrVar!=null && labelOrVar.position != subroutine.position)
|
if(labelOrVar!=null && labelOrVar.position != subroutine.position)
|
||||||
nameError(name, labelOrVar.position, subroutine)
|
nameError(name, labelOrVar.position, subroutine)
|
||||||
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
|
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
|
||||||
|
@ -62,7 +62,7 @@ class TestScoping {
|
|||||||
goto labeloutside
|
goto labeloutside
|
||||||
goto iflabel
|
goto iflabel
|
||||||
goto main.start.nested.nestedlabel
|
goto main.start.nested.nestedlabel
|
||||||
; goto nested.nestedlabel ; TODO should also work!!
|
goto nested.nestedlabel
|
||||||
}
|
}
|
||||||
iflabel:
|
iflabel:
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ interface IStatementContainer {
|
|||||||
fun isEmpty(): Boolean = statements.isEmpty()
|
fun isEmpty(): Boolean = statements.isEmpty()
|
||||||
fun isNotEmpty(): Boolean = statements.isNotEmpty()
|
fun isNotEmpty(): Boolean = statements.isNotEmpty()
|
||||||
|
|
||||||
fun searchLabelOrVariableNotSubscoped(name: String): Statement? { // TODO return INamedStatement instead? and rename to searchSymbol ?
|
fun searchLabelOrVariableNotSubscoped(name: String, alsoSubroutine: Boolean): Statement? { // TODO return INamedStatement instead? and rename to searchSymbol ?
|
||||||
// this is called quite a lot and could perhaps be optimized a bit more,
|
// this is called quite a lot and could perhaps be optimized a bit more,
|
||||||
// but adding a memoization cache didn't make much of a practical runtime difference...
|
// but adding a memoization cache didn't make much of a practical runtime difference...
|
||||||
for (stmt in statements) {
|
for (stmt in statements) {
|
||||||
@ -98,44 +98,48 @@ interface IStatementContainer {
|
|||||||
is Label -> {
|
is Label -> {
|
||||||
if(stmt.name==name) return stmt
|
if(stmt.name==name) return stmt
|
||||||
}
|
}
|
||||||
|
is Subroutine -> {
|
||||||
|
if(alsoSubroutine && stmt.name==name)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
is AnonymousScope -> {
|
is AnonymousScope -> {
|
||||||
val found = stmt.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is IfStatement -> {
|
is IfStatement -> {
|
||||||
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is BranchStatement -> {
|
is BranchStatement -> {
|
||||||
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is ForLoop -> {
|
is ForLoop -> {
|
||||||
val found = stmt.body.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is WhileLoop -> {
|
is WhileLoop -> {
|
||||||
val found = stmt.body.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is RepeatLoop -> {
|
is RepeatLoop -> {
|
||||||
val found = stmt.body.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is UntilLoop -> {
|
is UntilLoop -> {
|
||||||
val found = stmt.body.searchLabelOrVariableNotSubscoped(name)
|
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
is WhenStatement -> {
|
is WhenStatement -> {
|
||||||
stmt.choices.forEach {
|
stmt.choices.forEach {
|
||||||
val found = it.statements.searchLabelOrVariableNotSubscoped(name)
|
val found = it.statements.searchLabelOrVariableNotSubscoped(name, alsoSubroutine)
|
||||||
if(found!=null)
|
if(found!=null)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
@ -151,59 +155,80 @@ interface IStatementContainer {
|
|||||||
|
|
||||||
val allDefinedSymbols: List<Pair<String, Statement>>
|
val allDefinedSymbols: List<Pair<String, Statement>>
|
||||||
get() {
|
get() {
|
||||||
return statements.mapNotNull {
|
return statements.filterIsInstance<INamedStatement>().map { Pair(it.name, it as Statement) }
|
||||||
when (it) {
|
|
||||||
is Label -> it.name to it
|
|
||||||
is VarDecl -> it.name to it
|
|
||||||
is Subroutine -> it.name to it
|
|
||||||
is Block -> it.name to it
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface INameScope: IStatementContainer, INamedStatement {
|
interface INameScope: IStatementContainer, INamedStatement {
|
||||||
fun subScope(name: String): INameScope? = statements.firstOrNull { it is INameScope && it.name==name } as? INameScope
|
fun subScope(name: String): INameScope? = statements.firstOrNull { it is INameScope && it.name==name } as? INameScope
|
||||||
|
|
||||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? { // TODO return INamedStatement instead?
|
fun lookup(scopedName: List<String>, localScope: INameScope) : Statement? { // TODO return INamedStatement instead?
|
||||||
if(scopedName.size>1) {
|
return if(scopedName.size>1)
|
||||||
// a scoped name refers to a name in another module.
|
lookupQualified(scopedName, localScope)
|
||||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
else {
|
||||||
for(module in localContext.definingModule.program.modules) {
|
lookupUnqualified(scopedName[0], localScope)
|
||||||
var scope: INameScope? = module
|
}
|
||||||
for(name in scopedName.dropLast(1)) {
|
}
|
||||||
scope = scope?.subScope(name)
|
|
||||||
|
private fun lookupQualified(scopedName: List<String>, startScope: INameScope): Statement? {
|
||||||
|
// a scoped name refers to a name in another namespace.
|
||||||
|
// look "up" from our current scope to search for the correct one.
|
||||||
|
if(scopedName==listOf("nested", "nestedlabel"))
|
||||||
|
println("$scopedName") // TODO weg
|
||||||
|
|
||||||
|
// TODO FIX qualified lookup.
|
||||||
|
|
||||||
|
var statementScope = startScope
|
||||||
|
while(statementScope !is GlobalNamespace) {
|
||||||
|
if(statementScope !is Module && statementScope.name==scopedName[0]) {
|
||||||
|
// drill down to get the full scope
|
||||||
|
var scope: INameScope? = statementScope
|
||||||
|
for (name in scopedName.drop(1).dropLast(1)) {
|
||||||
|
scope = scope!!.subScope(name)
|
||||||
if (scope == null)
|
if (scope == null)
|
||||||
break
|
|
||||||
}
|
|
||||||
if(scope!=null) {
|
|
||||||
val result = scope.searchLabelOrVariableNotSubscoped(scopedName.last())
|
|
||||||
if(result!=null)
|
|
||||||
return result
|
|
||||||
return scope.subScope(scopedName.last()) as Statement?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
|
}
|
||||||
|
return scope!!.searchLabelOrVariableNotSubscoped(scopedName.last(), true)
|
||||||
} else {
|
} else {
|
||||||
// unqualified name
|
statementScope = (statementScope as Node).definingScope
|
||||||
// find the scope the localContext is in, look in that first
|
}
|
||||||
var statementScope = localContext
|
}
|
||||||
while(statementScope !is ParentSentinel) {
|
|
||||||
val localScope = statementScope.definingScope
|
// not found, try again but now assume it's a globally scoped name starting with the name of a block
|
||||||
val result = localScope.searchLabelOrVariableNotSubscoped(scopedName[0])
|
for(module in (startScope as Node).definingModule.program.modules) {
|
||||||
if (result != null)
|
module.statements.forEach {
|
||||||
return result
|
if(it is Block && it.name==scopedName[0])
|
||||||
val subscope = localScope.subScope(scopedName[0]) as Statement?
|
return it.lookup(scopedName, it)
|
||||||
if (subscope != null)
|
}
|
||||||
return subscope
|
|
||||||
// not found in this scope, look one higher up
|
|
||||||
statementScope = statementScope.parent
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun lookupUnqualified(name: String, startScope: INameScope): Statement? {
|
||||||
|
val builtinFunctionsNames = (startScope as Node).definingModule.program.builtinFunctions.names
|
||||||
|
if(name in builtinFunctionsNames) {
|
||||||
|
// builtin functions always exist, return a dummy placeholder for them
|
||||||
|
val builtinPlaceholder = Label("builtin::$name", startScope.position)
|
||||||
|
builtinPlaceholder.parent = ParentSentinel
|
||||||
|
return builtinPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// search for the unqualified name in the current scope (and possibly in any anonymousscopes it may contain)
|
||||||
|
// if it's not found there, jump up one higher in the namespaces and try again.
|
||||||
|
var statementScope = startScope
|
||||||
|
while(statementScope !is GlobalNamespace) {
|
||||||
|
val symbol = statementScope.searchLabelOrVariableNotSubscoped(name, true)
|
||||||
|
if(symbol!=null)
|
||||||
|
return symbol
|
||||||
|
else
|
||||||
|
statementScope = (statementScope as Node).definingScope
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// private fun getNamedSymbol(name: String): Statement? =
|
||||||
|
// statements.singleOrNull { it is INamedStatement && it.name==name }
|
||||||
|
|
||||||
val containsCodeOrVars get() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm" }
|
val containsCodeOrVars get() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm" }
|
||||||
val containsNoCodeNorVars get() = !containsCodeOrVars
|
val containsNoCodeNorVars get() = !containsCodeOrVars
|
||||||
}
|
}
|
||||||
@ -263,7 +288,7 @@ class Program(val name: String,
|
|||||||
private val _modules = mutableListOf<Module>()
|
private val _modules = mutableListOf<Module>()
|
||||||
|
|
||||||
val modules: List<Module> = _modules
|
val modules: List<Module> = _modules
|
||||||
val namespace: GlobalNamespace = GlobalNamespace(modules, builtinFunctions.names)
|
val namespace: GlobalNamespace = GlobalNamespace(modules)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// insert a container module for all interned strings later
|
// insert a container module for all interned strings later
|
||||||
@ -414,7 +439,7 @@ open class Module(final override var statements: MutableList<Statement>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GlobalNamespace(val modules: Iterable<Module>, private val builtinFunctionNames: Set<String>): Node, INameScope {
|
class GlobalNamespace(val modules: Iterable<Module>): Node, INameScope {
|
||||||
override val name = "<<<global>>>"
|
override val name = "<<<global>>>"
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
override val statements = mutableListOf<Statement>() // not used
|
override val statements = mutableListOf<Statement>() // not used
|
||||||
@ -427,22 +452,6 @@ class GlobalNamespace(val modules: Iterable<Module>, private val builtinFunction
|
|||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
throw FatalAstException("cannot replace anything in the namespace")
|
throw FatalAstException("cannot replace anything in the namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, localContext: Node): Statement? { // TODO return INamedStatement instead?
|
|
||||||
if (scopedName.size == 1 && scopedName[0] in builtinFunctionNames) {
|
|
||||||
// builtin functions always exist, return a dummy localContext for them
|
|
||||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
|
||||||
builtinPlaceholder.parent = ParentSentinel
|
|
||||||
return builtinPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup something from the module.
|
|
||||||
return when (val stmt = localContext.definingModule.lookup(scopedName, localContext)) {
|
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
|
||||||
null -> null
|
|
||||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
object BuiltinFunctionScopePlaceholder : INameScope {
|
||||||
|
@ -730,7 +730,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
||||||
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
|
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
|
||||||
else
|
else
|
||||||
program.namespace.lookup(nameInSource, this)
|
program.namespace.lookup(nameInSource, this.definingScope)
|
||||||
|
|
||||||
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
||||||
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
||||||
@ -747,7 +747,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val node = program.namespace.lookup(nameInSource, this)
|
val node = program.namespace.lookup(nameInSource, this.definingScope)
|
||||||
?: throw UndefinedSymbolError(this)
|
?: throw UndefinedSymbolError(this)
|
||||||
val vardecl = node as? VarDecl
|
val vardecl = node as? VarDecl
|
||||||
if(vardecl==null) {
|
if(vardecl==null) {
|
||||||
|
@ -395,7 +395,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
|
|
||||||
fun inferType(program: Program): InferredTypes.InferredType {
|
fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
if (identifier != null) {
|
if (identifier != null) {
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, this) ?: return InferredTypes.unknown()
|
val symbol = program.namespace.lookup(identifier!!.nameInSource, this.definingScope) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +1,85 @@
|
|||||||
|
|
||||||
|
%import syslib
|
||||||
|
%import textio
|
||||||
|
%zeropage basicsafe
|
||||||
|
%option no_sysinit
|
||||||
|
|
||||||
|
; Create a custom character set on the C64.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
goto labeloutside
|
txt.color(1)
|
||||||
|
txt.print("creating charset...\n")
|
||||||
|
charset.make_custom_charset()
|
||||||
|
|
||||||
if true {
|
; activate the new charset in RAM
|
||||||
if true {
|
ubyte block = c64.CIA2PRA
|
||||||
goto labeloutside
|
const ubyte PAGE1 = ((c64.Screen >> 6) & $F0) | ((charset.CHARSET >> 10) & $0E)
|
||||||
goto iflabel
|
|
||||||
|
c64.CIA2PRA = (block & $FC) | (lsb(c64.Screen >> 14) ^ $03)
|
||||||
|
c64.VMCSB = PAGE1
|
||||||
|
|
||||||
|
txt.print("\n @ @ @ @\n")
|
||||||
}
|
}
|
||||||
iflabel:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat {
|
charset {
|
||||||
goto labelinside
|
const uword CHARSET = $2000
|
||||||
labelinside:
|
|
||||||
|
sub copy_rom_charset() {
|
||||||
|
; copies the charset from ROM to RAM so we can modify it
|
||||||
|
|
||||||
|
sys.set_irqd()
|
||||||
|
ubyte bank = @($0001)
|
||||||
|
@($0001) = bank & %11111011 ; enable CHAREN, so the character rom accessible at $d000
|
||||||
|
sys.memcopy($d000, CHARSET, 256*8*2) ; copy the charset to RAM
|
||||||
|
|
||||||
|
@($0001) = bank ; reset previous memory banking
|
||||||
|
sys.clear_irqd()
|
||||||
}
|
}
|
||||||
|
|
||||||
labeloutside:
|
sub make_custom_charset() {
|
||||||
|
copy_rom_charset()
|
||||||
|
|
||||||
|
; make all characters italic
|
||||||
|
ubyte c
|
||||||
|
for c in 0 to 255 {
|
||||||
|
uword ptr = CHARSET + c*$0008
|
||||||
|
@(ptr) >>= 2
|
||||||
|
@(ptr+1) >>= 2
|
||||||
|
@(ptr+2) >>= 1
|
||||||
|
@(ptr+3) >>= 1
|
||||||
|
;@(ptr+4) >>= 0
|
||||||
|
;@(ptr+5) >>= 0
|
||||||
|
@(ptr+6) <<= 1
|
||||||
|
@(ptr+7) <<= 1
|
||||||
|
|
||||||
|
ptr = CHARSET + 256*8 + c*$0008
|
||||||
|
@(ptr) >>= 2
|
||||||
|
@(ptr+1) >>= 2
|
||||||
|
@(ptr+2) >>= 1
|
||||||
|
@(ptr+3) >>= 1
|
||||||
|
;@(ptr+4) >>= 0
|
||||||
|
;@(ptr+5) >>= 0
|
||||||
|
@(ptr+6) <<= 1
|
||||||
|
@(ptr+7) <<= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
start:
|
; add a smiley over the '@'
|
||||||
start:
|
|
||||||
start:
|
ubyte[] smiley = [
|
||||||
|
%00111100,
|
||||||
|
%01000010,
|
||||||
|
%10100101,
|
||||||
|
%10000001,
|
||||||
|
%10100101,
|
||||||
|
%10011001,
|
||||||
|
%01000010,
|
||||||
|
%00111100
|
||||||
|
]
|
||||||
|
|
||||||
|
sys.memcopy(smiley, CHARSET, len(smiley))
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user