Sunday, March 18, 2018

Cursor Control with Commodore 64.

The ability to draw a character at any spot on the screen - within the constraint of rows and columns, perhaps, is key, when you're trying to create either a text editor, or a roguelike, or nearly anything with a text-based UI.

It's apparently also impossible on the Apple I, but fortunately I'm working with something a bit more advanced. I'm not doing THAT retro.

C64 Programmer's Reference Guide is fairly good. I'm not entirely sure I could have comprehended it, or would have had access to it when I was in grade school, but it's at least theoretically possible.

I definitely would have been able to get this book, whether via a library or by pestering my parents sufficiently. Not sure I had the character and audacity required for such a proactive behavior, though, but meh.

In any case, the various bits of secret sauce I've discovered thus far are:

poke 214, row
poke 211, column:
sys (58732) 

This sets the cursor to a specific row and column, as far as printing is considered. The SYS is required to set the row and column.

The get loop is fairly common, being
get a$:if a$="" then goto
You can find that in many different sources. However, it does not display the cursor.  Poking at the right spot (204, 0 for enable, 1 for disable) does turn the cursor on and off, but for some reason, the cursor location is wrong.

You can also do direct manipulation of screen memory by poking at locations from 1024 onwards, but unfortunately the character ROM doesn't directly map to PETSCII.

As a downside of using GET input, you do not get to benefit from the, actually rather nifty on-screen editing that is default for C64.

I'm trying to create a status line, by jumping between cursor location and line 23, where I print current cursor location and... well, haven't really thought that far. Filename, I suppose. Currently I'm just putting the last key's code there.

I flip editor mode on F1, of course the only command currently supported is "save", and frankly, there is no scrolling, so max document length is 25 lines, but, baby steps.

print#, which is used to write to files, is also... finicky, I guess. it uses slightly different printing rules, and it differentiates between variables by CRs (char code 13), which makes it unsuited to printing numbers, unless you use some sort of string encoding. I did figure out I could save ASM files as hex strings, and loading those would be quite simple. I might use that as an intermediate step.

Anyway, the various SYS commands are fortunately documented in the Programmer's Reference Guide.

I am currently working with the idea that actual data is kept on known area in memory, which is then written to disk. I've already run into some issues with the memory area not being cleared, which ended up with this little bit of... fun.
a900a2009d00109dff109dff119dff12e8e0ffd0ef60
 To break it down:
a900     lda 0
a200     ldx 0
9d0010   sta 4096,x
9dff10   sta 4351,x
9dff11   sta 4606,x 
9dff12   sta 4861,x
e8       inx   
e0ff     cpx 255
d0ef     bne -16
60       rts

It's really moderately straight-forward, it should zero an area from 4096 to 5116, or so, by copying accumulator to various memory slots adjusted by x-index. it increments x-index by 1, then compares it to 255, and if it's not there yet, it jumps back 16 bytes in execution.
Biggest new thing I learned was that the comparison operator jumps backwards or forwards by a specific number of bytes. Makes sense.

Oh, I guess I should mention I tested this by using this little program here :
ready.
list

10 rem hex8todec
30 i=1:m=0
35 input "mem address start:";s
40 print str$(s);:input a$
50 gosub 150:i=i+1
60 d=b*16
70 gosub 150
80 b=d+b
90 print b,;
100 poke s+m, b:m=m+1
110 if i=len(a$) then goto 200
120 i=i+1
140 goto 50
150 b=asc(mid$(a$,i,1))
160 if b<64 then b=b-48
170 if b>64 then b=b-55
180 return
200 rem end
ready.

Running the program with an appropriate sys address call, of course.
There are a couple of different ways of doing byte to hex conversion and vice versa.
A lookup table works fine, and is pretty concise for byte-to-hex, since the and masking and division need to be done anyway. I don't worry much about the memory limitations at this point, although they are a concern with real-world applications. My editor program is starting to reach the point where putting the screen at 4096 may risk overwriting the actual code.

For hex-to-byte conversion, I guess you could create an array of integers of the PETSCII codes, and compare each letter to the array, recording the index of the match, but I'm not convinced it's faster than what I have here. Might use less memory, though.

No comments: