ASCII to C64 Screen Codes In DASM Assembler - string

I'm learning assembly for the 6502 micro via a C64 emulator. Currently trying to output strings to the screen. Here is my code:
processor 6502
org $1000
ldx #$00 ;using x register as column counter
print:
lda message,x;load a with x bit from message
sta $0400,x ;store this bit in row 0 col 0 address
inx ;x++
cpx #$05 ;is x >= 5?
bne print ;if not x >= 5, loop again
rts ;return from program
message dc "HELLO"
hexmsg dc $08,$05,$0c,$0c,$0f
Because my editor (notepad++ on win 10) uses ascii-like char-codes, the "HELLO" in message is bits 48 45 4C 4C 4F. This gives the following output in the top left corner of the screen:
This is correct I guess, by looking at the commodore screen codes table here.
If I change line 6 to lda hexmsg,x then I get exactly what I'm after, the word HELLO.
I'm not yet very familiar with DASM assembler and having trouble finding complete documentation of it (if it exists). Most tutorials I've found just have you declare message .byte "HELLO" or something similar and it just works because the assembler they are using auto-converts the ascii-like text string to a commodore string automatically, but DASM doesn't seem to do that.
Does anyone know a way I can either get DASM to do this, or recommend another way to simply type strings into the assembler rather than manually typing my strings as a bunch of hex data?

Aha, ASCII encoding vs. Commodore screen codes. We've all been there. You have a few options:
Don't write directly to screen memory, but use the Kernal CHROUT routine instead (probably via the higher-level string-output routine). Then all you have to worry about is the differences between ASCII and PETSCII, but that's a story for another bed-time. Also, this is good for text but sucks for games as the Kernal is s-l-o-w compared to direct writes.
Write a little conversion routine that runs when your program starts, eats your string table, and spits out converted screen code equivalents. Fast and efficient, provided your strings are all together and you're not writing a ROM-based application (which would not be able to do an in-place conversion).
Write a DASM pre-processor which runs before DASM in your build script and basically does the same conversion as #2 above, but to the sourcecode before the assembler sees it. This can be a bit gnarly, and you have to be sure to back-up the original source before munging it.
Get the DASM sourcecode and patch it to invoke a user-exit for a new data-type (for screen codes) that does the same as #2 but on-the-fly during assembly. Very gnarly.
Use lower-case letters in your strings, which will translate to upper-case screen code equivalents during assembly. You may have overlooked the fact that what you're seeing is the shifted representation of the characters in the string, which in the default display mode is graphics symbols.
Speaking from experience of all 5 options, I've settled on #2.
Also: switch to KickAssembler, which
is newer, and actively maintained
offers more powerful features
integrates very well with VICE for debugging

Been a while since I programmed 6510. (If you are not pressed for conserving every single byte of C64 memory..) Also consider zero terminating your string(s) with say a 0 byte rather than terminating on length reached in the X register. Makes it a little more convenient rather than working out string lengths :D
processor 6502
org $1000
printstring:
ldx #$00
printstrlp:
lda message,x
cmp #0
beq quitstr
cmp #32 ;' ' character
beq noconv
cmp #33 ;! character
beq noconv
cmp #42 ;* character
beq noconv
cmp #48 ;numbers 0-9
bcs numconv
conv:
sec
sbc #$40
noconv:
sta $0400,x
inx
bne printstrlp
quitstr:
rts
numconv:
cmp #58
bcc noconv
jmp conv
message dc "** HELLO C64 WORLD! **",0

Here is patched version of DASM aseembler.
http://iancoog.altervista.org/C/dasm2.20.07-iAN_Rev_N.rar
You can use SCRU and SCRL directives to convert ASCII->Screencode conversion.
label SCRU "string"
label SCRL "string"
SCRU is for making uppercase text even if entered lowercase. SCRL keeps casing.

if it can help, here is a little python script to do it.
Just call it with python3 str_conv.py code.asm message
import argparse
import os
mapping = [
'#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '~', ']', '|', '\\', ' ', '!', '"', '#', '$', '%', '&',
'\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
':', ';', '<', '=', '>', '?'
]
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Convert ASCII to C64 screen codes')
parser.add_argument('file', help='assembly source code')
parser.add_argument('label', help='label used in DASM')
args = parser.parse_args()
filepath = os.path.join(os.getcwd(), args.file)
backup = os.path.join(os.getcwd(), args.file) + ".bck"
os.rename(filepath, backup)
label = args.label
new_code = []
found = False
for line_nb, line in enumerate(open(backup)):
if line.find("\\b{}\\b".format(label)) and '"' in line:
text = line[line.find('"')+1:line.rfind('"')]
new_line = "{}:\t.byte ".format(label)
for i, char in enumerate(text):
new_line += "{}{}".format(mapping.index(char), ", " if i != len(text)-1 else "\n")
print(line_nb, new_line)
new_code.append("; {}".format(line))
new_code.append(new_line)
found = True
else:
new_code.append(line)
if not found:
print("Label {} not found!".format(label))
else:
with open(filepath, "w") as new_file:
for line in new_code:
new_file.write(line)
print("Done!")

Related

Unable to append to array element

I'm trying to append a string to the end of an array element by using the following function:
def spread(farm):
#writing potential fruit spread
for y in range(0,len(farm[0])):
for x in range(0,len(farm[0])):
#if this current space is a tree, write the potential spaces accordingly
if farm[y][x] == "F" or farm[y][x] == "W" or farm[y][x] == "G" or farm[y][x] == "J" or farm[y][x] == "M":
for b in [-1,0,1]:
#making sure the y-coord is within the bounds of the farm
if y+b >= 0 and y+b < len(farm[0]):
for a in [-1,0,1]:
#making sure the x-coord is within the bounds of the farm and the selected space is not a tree
if x+a >= 0 and x+a < len(farm[0]) and farm[y+b][x+a] != "F" and farm[y+b][x+a] != "W" and farm[y+b][x+a] != "G" and farm[y+b][x+a] != "J" and farm[y+b][x+a] != "M":
#if space is blank, write over the space outright
if farm[y+b][x+a] == "_":
farm[y+b][x+a] = farm[y][x].lower()
else:
#wherein my troubles lie :(
farm[y+b][x+a] = farm[y+b][x+a] + farm[y][x].lower()
return farm
with the following input, an array (in farm):
[['_' '_' '_' 'F' '_' '_' '_']
['_' '_' '_' 'W' '_' '_' '_']
['_' '_' '_' '_' '_' '_' '_']
['_' '_' '_' 'J' '_' '_' '_']
['_' '_' '_' '_' '_' '_' '_']
['_' 'G' '_' '_' '_' 'F' '_']
['W' '_' '_' '_' '_' '_' 'G']]
What the function is supposed to do is to simulate spreading fruit trees. Every tree (represented by a capital letter) will spread to the adjacent squares (represented by a lowercase character or underscore). However, the very last line handles the case in which the selected array element is not an underscore. What is supposed to happen is that it will append the string to the end of the array element instead of replacing it, but instead appends nothing. The output is supposed to look something like this:
[['_' '_' 'fw' 'F' 'fw' '_' '_']
['_' '_' 'fw' 'W' 'fw' '_' '_']
['_' '_' 'wj' 'wj' 'wj' '_' '_']
['_' '_' 'j' 'J' 'j' '_' '_']
['g' 'g' 'jg' 'j' 'jf' 'f' 'f']
['gw' 'G' 'g' '_' 'f' 'F' 'fg']
['W' 'gw' 'g' '_' 'f' 'fg' 'G']]
But instead it outputs this:
[['_' '_' 'f' 'F' 'f' '_' '_']
['_' '_' 'f' 'W' 'f' '_' '_']
['_' '_' 'w' 'w' 'w' '_' '_']
['_' '_' 'j' 'J' 'j' '_' '_']
['g' 'g' 'j' 'j' 'j' 'f' 'f']
['g' 'G' 'g' '_' 'f' 'F' 'f']
['W' 'g' 'g' '_' 'f' 'f' 'G']]
What am I doing wrong?
As noted, Numpy has its own string type, which limits the length of the contained text, so that the data can be stored in a neat "rectangular" way without indirection. (This means for example that Numpy can simply do math to calculate where any element will be, rather than chasing pointers for multiple indices.)
It is possible to work around this by explicitly specifying dtype=object when we create the array. This means that Numpy will store pointers to Python objects in its internal representation; this loses a lot of the benefits, but may still allow you to write overall faster and more elegant code depending on the task.
Let's try to implement that here. My first suggestion will be to use empty '' strings for the empty spots on the farm, rather than '_'; this removes a bunch of special cases from our logic (and as we all know, "special cases aren't special enough to break the rules").
Thus, we start with:
farm = np.array([
['', '', '', 'F', '', '', ''],
['', '', '', 'W', '', '', ''],
['', '', '', '', '', '', ''],
['', '', '', 'J', '', '', ''],
['', '', '', '', '', '', ''],
['', 'G', '', '', '', 'F', ''],
['W', '', '', '', '', '', 'G']
], dtype='object')
The primary way that Numpy helps us here is that it can efficiently:
Apply operatiors and functions to each element of the array elementwise.
Slice the array in one or more dimensions.
My approach is as follows:
Create a function that tells us what saplings get planted from the local tree.
Use Numpy to create an array of all the saplings that get planted from their corresponding trees, across the farm.
Create a function to plant saplings at a location offset from their source trees, by slicing the sapling array and "adding" (+, but it's string concatenation of course) the new saplings to a corresponding slice of the farm.
Iterate over the directions that the saplings can be planted, to do all the planting.
So, let's go through that....
The first step is pretty straightforward:
# Determine the saplings that will be planted, if any, from a given source plot.
# Handling the case where multiple trees are already present, is left as an exercise.
def sapling_for(plot):
return plot.lower() if plot in 'FGJMW' else ''
Now we need to apply that to the entire array. Applying operators like + is automatic. (If you have two arrays x and y with the same number of dimensions and the same size in each dimension, you can just add them with x + y and everything is added up elementwise. Notice that x * y is not "matrix multiplication", but element-wise multiplication.) However, for user-defined functions, we need a bit of extra work - we can't just pass our farm to sapling_for (after all, it doesn't have a .lower() method, for just one of many problems). It looks like:
saplings = np.vectorize(sapling_for)(farm)
Okay, not too difficult. Onward to the slicing. This is a bit tricky. We can easily enough get, for example, the north-west slice of the saplings: it is saplings[:-1, :-1] (i.e., everything except the last row and column). Notice we are not doing two separate index operations - this is Deep NumPy Magic (TM), and we need to do things NumPy's way.
My idea here is that we can represent saplings "spreading" to the southeast by taking this northwest slice and adding it to a southeast slice of the farm: farm[1:, 1:] += saplings[:-1, :-1]. We could simply do that eight times for each compass direction of spread. But what about a generalization?
It's a little trickier, since e.g. 1: doesn't mean anything by itself. It does, however, have a built-in Python representation: the native slice type, which we can also use for Numpy indexing (and for indexing built-in sequence types, too!). So I wrote a helper function to create those:
def get_slice(dx):
return slice(dx, None, None) if dx >= 0 else slice(None, dx, None)
These are similar to range objects: the parameters are the start point, end point and "step". The idea here is that a negative value will give a slice taking that many items off the end, while a positive value will take them off the front.
That lets us write a general function to add a slice of one array to a shifted position (in the opposite corner) of a "base" array:
def add_shifted(base, to_add, dx, dy):
base[get_slice(dx), get_slice(dy)] += to_add[get_slice(-dx), get_slice(-dy)]
Hopefully the logic is clear enough. Finally, we can iterate over all the (dx, dy) pairs that make sense for spreading the saplings: everywhere within one space, except for (0, 0).
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
if dx != 0 or dy != 0:
add_shifted(farm, saplings, dx, dy)
And we're done.
A NumPy array with a string dtype will silently truncate any strings you try to store that are too big for the dtype. Use a list of lists.

Trying to create a for loop that checks my file name for backslashes and replaces them with forward slashes

As mentioned in the title I am trying to create a function to parse through a string, which would be my file name and return another version but with all the backslashes replaced with forward slashes. My file names are saved with backslashes instead of forward slashes and thus does not work unless I use 'r' before the file name. I know this is an easy workaround but I am now interested in defining a function to fix this solution.
Here is the code I am attempting to use:
backslash = '\''
def parser(string, character):
letters = []
for i in string:
if i != character:
letters.append(i)
else:
letters.append('/')
return letters
This is my output, which is obviously wrong. Does anyone have any ideas how I can fix my issue or a way to circumvent this?
[B',
'o',
'b',
'\\',
'g',
'o',
'e',
's',
'\\',
's',
'h',
'o',
'p',
'p',
'i',
'n',
'g']
p.s. If it makes any difference I am using windows 10 and microsoft.
The backslash ( \) character is used to escape characters that
otherwise have a special meaning, such as newline, backslash itself,
or the quote character
Your backslash character is holding ', which is not correct. For your variable to hold the backslash literal itself, you must use two backslash as given in below code.
The following code parses correctly -
backslash = '\\'
def parser(string, character):
letters = []
for i in string:
if i != character:
letters.append(i)
else:
letters.append('/')
return letters
# The address location to be parsed
address_with_backslash = 'C:\\user\\something\\InvestingScientist'
print("Original address : " + address_with_backslash)
print("\nAddress after Parsing : " + "".join(parser(address_with_backslash,backslash)))
Output :
Original address : C:\user\something\InvestingScientist
Address after Parsing : C:/user/something/InvestingScientist
Hope this helps!
backslash is the escape character so if you want to have a literal backslash in a string you need to use a double-backslash
Heres the solution:
backslash = '\\'
def parser(string, character):
letters = []
for i in string:
if i != character:
letters.append(i)
else:
letters.append('/')
return letters

If you have a fixed list size, would this code be O(1)

Say for example you are looping through letters in a list, but you have to check for punctuation. Would the following code still be O(n), n being the max characters in a line? I think this because the punctuation list is a fixed size, so that if statement would still be O(1) right?
punctuation = [',', '.', '?', '!', ':', ';', '"', ' ', '\t', '\n']
for letters in line:
if letters not in punctuation:
word += letters
Yes you're right, since the punctuation list is fixed in size (and not dependent on N), the overall time complexity of your code should be O(N).
As other commentators have pointed out, O(N*M) would probably be more precise, with N being the number of characters you are reading in total, and M the number of punctuation characters.
If you want to optimize there, you could store the punctuation characters in a set, where in operates in constant time:
punctuation = {',', '.', '?', '!', ':', ';', '"', ' ', '\t', '\n'}
for letter in line:
if letter not in punctuation:
word += letter

How to store string characters to a list in an order that matches a randomly selected word beforehand for advanced Hangman game

I am a student that took one class of Java Freshman year (am now a senior). This summer I decided to start coding again. I am programming all the past projects that I miserably failed at in my CompSci class.
I decided to start learning python, and it's slowly coming back to me.
Anyways, I am working on a hangman game. I made a list
wordList = ['beloit', 'friend', 'transcript', 'feeling', 'internship',
'guarding', 'feeling']
which I randomly select a word using the random.choice() function.
Here is the rest of my code:
while guesses == 0:
letter = input('Please guess a letter: ')
print('Letter is: ' + letter)
if letter not in word:
print('Nice guess, but wrong')
failure -= 1
print('You have {} trie(s) left.'.format(failure))
if failure == 0:
print('You lost')
break
elif letter in word:
print(letter + ' is in the secret word!')
storeList.append(letter)
print(storeList)
After running a few times, while purposefully showing the random word "guarding" my output is:
guarding
Please guess a letter: b
Letter is: b
Nice guess, but wrong
You have 14 tries left.
Please guess a letter: d
Letter is: d
d is in the secret word!
['d']
Please guess a letter: g
Letter is: g
g is in the secret word!
['d', 'g']
Please guess a letter: u
Letter is: u
u is in the secret word!
['d', 'g', 'u']
I am trying to store the input variables from 'letter' in the correct order so it shows
['g', 'u', '', '', 'd'] ...
I am not necessarily looking for a full answer, but just some hints on the methods I should use to achieve this.
Any other advice is appreciated, thanks again
You could initialise the list as a series of ""s that line up with your word's len(). (["","","","",""]) and then if they get it write, look up where the letter is and then address the list and add it (list[1] = "o"), then the user might guess c and you could check where that is and then add it with list[0] = c etc. (I'm using the word code as an example). :)

How can I test for the index that a letter is in in a string, and use this to replace parts of another string?

So I would have the user enter a letter, then I can also test to see if the letter is in a certain word. However, what I aim to do is to obtain the index of the letter in the word, e.g. if the user enters 'e' and my word is 'great' then it would output '2'. Additionally, I want to use this information to replace parts of another string with the same indexes as the letter. Like hangman, so if the output is '2' then say I have another string, 'balloon' or '******', I want to replace the first 'l' or the according '*' (2nd index) with 'e'.
Python Strings are immutable, so we need to work with them as lists
>>> string1 = "great"
>>> string2 = "balloon"
>>> char = "e"
>>> string2 = list(string2)
>>> string2[string1.index(char)] = char
>>> string2
['b', 'a', 'e', 'l', 'o', 'o', 'n']
>>> string2 = ''.join(string2)
'baeloon'

Resources