Edit for C128

Started by xlar54, January 06, 2008, 06:01 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

xlar54

Ok, I finally quit being lazy and created a C128 page on my website so I can publish code Im toying with.  Right now I have the current snapshot for the ms-dos like editor for the 128.  Guys, Im no expert, and this is my first big project with ML, so thats my disclaimer.  It doesnt do much right now other than set up the editor screen and allow text entry. Once I get all the character and scrolling routines in, Ill start work on the menu systems.  The source is included, so feel free to red-pen it and let me know what I could be doing better.  Thanks

http://www.scotthutter.com/c128

nikoniko

Looks like you're off to a good start! Haven't had more than a cursory look at it so far, but first thing I'd suggest is fixing a bug in the column increment code. I'm sure you've noticed it that it jumps from 9->20, 29->40, etc. The culprit is that the carry flag is being set by the CMP #$3A instruction, and then that carry is getting added to your tens column when the ADC is executed.

If you split TXTCOL into TXTCOLA and TXTCOLB, you could simplify your routine a bit. Eg.,

INC_COLCTR:

  LDA TXTCOLB
  CMP #$39
  BEQ INC_COLTENSDIGIT
  INC TXTCOLB
  RTS

INC_COLTENSDIGIT:

  LDA #$30
  STA TXTCOLB
  INC TXTCOLA
  RTS

Of course, this code assumes you'll check elsewhere to make sure your column value isn't larger than 99.

airship

Looks sweet. This is EXACTLY the kind of thing I've been wishing for in this thread.
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

xlar54

Holy schnikees... I was wondering what was going on in there... i knew it had to be something weird like that.  Before, I was doing :

ADC #$00

and that was adding a 1 to the value, which is what I wanted, but I didnt understand why adding ZERO was actually adding 1!  Thanks Nikoniko, I will make that fix keeping the carry flag in mind.

The scrolling routines are what really spook me, and Im moving slowly in that direction.  I dont want to have a visible scroll... on the VIC-II, I would just do some fancy block move in another buffer, then swap the screens, but Im not sure if this can be done on the VDC. Surely Commodore did it some how, so its a matter of searching the net for some VDC text scrolling routines, or worse - review the ROM routines.  But thanks for the feedback!

Quote from: nikonikoLooks like you're off to a good start! Haven't had more than a cursory look at it so far, but first thing I'd suggest is fixing a bug in the column increment code. I'm sure you've noticed it that it jumps from 9->20, 29->40, etc. The culprit is that the carry flag is being set by the CMP #$3A instruction, and then that carry is getting added to your tens column when the ADC is executed.

If you split TXTCOL into TXTCOLA and TXTCOLB, you could simplify your routine a bit. Eg.,

INC_COLCTR:

  LDA TXTCOLB
  CMP #$39
  BEQ INC_COLTENSDIGIT
  INC TXTCOLB
  RTS

INC_COLTENSDIGIT:

  LDA #$30
  STA TXTCOLB
  INC TXTCOLA
  RTS

Of course, this code assumes you'll check elsewhere to make sure your column value isn't larger than 99.
Edit Minutes Later:

Adding a CLC in there fixed the column count!  Thanks Nikoniko!

xlar54

Quote from: airshipLooks sweet. This is EXACTLY the kind of thing I've been wishing for in this thread.
Thanks Airship.. One of the things I really like about DOS is that editor.  I thought about adding a BASIC loader (10 SYS XXXX) but I really like the way it can remain in memory for when its needed like this.  Maybe two versions.  Anyway, its slowly coming together.  Will keep you posted.

nikoniko

Way down the road, after the main features are done, maybe you could even add the ability to edit BASIC programs. :D Although I like the 128's editor, there are times when it'd be nice to scroll back and forth, do search and replace, etc.

nikoniko

Quote from: xlar54Adding a CLC in there fixed the column count!  Thanks Nikoniko!
You're welcome.  Have fun with your project! The 128 could really use an editor like yours.

xlar54

Quote from: nikonikoWay down the road, after the main features are done, maybe you could even add the ability to edit BASIC programs. :D Although I like the 128's editor, there are times when it'd be nice to scroll back and forth, do search and replace, etc.
oooooooh..... YES.  Thats a great idea.  Another good reason to keep it somewhere else in memory!  Shouldnt take much to detokenize a loaded program, and then shuttle the listing into the editor's buffer.  The reverse might be a little tricky, but definitely worth the effort.

xlar54

New code has been posted.  Im now making use of BASIC's windowing system (ie WINDOW a,b,c,d via ML) to help with scrolling, and the line/col counter bugs have been worked out.  Its actually starting to feel like an editor now.  I skimmed through the "Mapping the Commodore 128" PDF book and found alot of helpful routines so I dont have to roll my own cursor, etc. And I also found some peek-like and poke-like routines for the VDC online elsewhere. I guess when in doubt, let BASIC do it for ya.

nikoniko

Quick note: you changed your file name to EDIT128.ZIP, but your page is still linked to EDITOR128.ZIP.

One thing to be mindful of when using the ROM routines is that they may do more than you want them to. For instance, when you type a quote character in your editor, BASIC's quote mode gets switched on.

By the way, did you see my follow-up on your FAST mode post?

http://landover.no-ip.com/128/viewtopic.php?id=1868

BigDumbDinosaur

Interesting project.  Although I'm no fan of MS-DOS or any of that other Microsoft crud, I've always found the DOS 6 style editor one of the better ones.

As you develop your project, something you might wish to consider is externalizing references in your source code.  You have a lot of hard coded references to kernal zero page addresses (entered as hex values) embedded in the source code, which is contrary to good programming practice.  Addresses, magic numbers, etc., should be externalized (e.g., defined in an "include" file) so the source code is more accessible to someone who is studying it.  For example, an instruction like STA $E8 essentially tells the casual reader nothing of value, whereas STA LSXP tells me you are setting the display row on which input is to be accepted.

In other words, with hard coded references, reading your source is going to be difficult to understand for someone who doesn't know the 128 architecture, which I'm sure is not your intention.  With all references externalized and labeled per the official 128 documentation, someone who knows the 8502 assembly language and is familiar with the machine will be able to readily decipher your work and perhaps help you out with finding an obstinate bug, or perhaps suggest a worthwhile improvement.

Other than that, it looks like you are off to a great start.  Just as a joke, you could put in a reference to the A-drive, which I'm sure will cause a few double-takes.  :-)
x86?  We ain't got no x86.  We don't need no stinking x86!

BigDumbDinosaur

By the way, you can speed up your video management routines by bypassing the "traditional" kernal jump table and working instead with the screen editor ROM's jump table, which starts at $C000.  For example:

BASOUT ($FFD2): use JPRINT at $C00C

BASIN ($FFCF): use JGETSCRN at $C009

PLOT ($FFF0): use JPLOT at $C018

To manipulate the location of the VDC's hardware cursor, use JCRSR80 at $C01B.  Set the absolute, zero-based  row and column coordinates at TABLX ($EB) and PNTR ($EC), respectively, in zero page, and then call JCRSR80.  The cursor will instantly move to your new coordinates.

As a practical matter, you may eventually find it more advantageous to do all of your display manipulation from within your own code and not use most of the editor ROM.  There is a lot of checking in the ROM to determine whether the 40 or 80 column display is active, all of which noticeably increases the time required to update the display, especially when the screen must be scrolled.  However, even if you stay with the editor ROM functions, you will find that your display will be more frisky than it would be going through the kernal ROM, since you will be avoiding the latter's overhead in checking to see which device is the current input or output device.  Since you already know which device is the target, going directly through the screen editor ROM removes the device checks and shortens the time required to execute the operation.

Lastly, to maximize performance while updating the display (which is the most labor-intensive operation in any text editor), temporarily kill VIC raster interrupts and memory bus cycles as follows:
lda clkrate                 ;$D030 in the VIC
ora #%00000010       ;toggle "test" bit
sta clkrate                 ;kill raster IRQ & bus access DMA

After the display update has finished, restore VIC operation:
lda clkrate
and #%11111101       ;toggle "test" bit
sta clkrate                  ;restart raster IRQ & bus access DMA

When the test bit is set in register $30, the VIC will stop generating the raster IRQs and will cease bus accesses (other than DRAM refreshes).  Under this condition, the 8502 has 100 percent access to the address and data buses, and therefore can manipulate the VDC much faster.  Note that when raster IRQs are off there is no jiffy IRQ and therefore no keyboard scanning.  Obviously, you don't want to try this from immediate mode, as you will not be able to restore the raster IRQ -- the keyboard will be dead.  
x86?  We ain't got no x86.  We don't need no stinking x86!

xlar54

Excellent suggestions - too many to comment on at once, but i really appreciate the feedback.  I continue to learn more and more regarding ML development, and the feedback really helps.  Sometimes I think just jumping into a larger project head-first is not at all a bad thing.  Sticking with subroutines, and trying to keep things relatively modular.  Occasionally you run into a hard to find bug, like the ADC issue nikoniko helped me with earlier..those are little kicks in the hiney to pay attention to opcode details.  Being an ML programmer back in the day would have been quite an interesting and educational job.  Ill go over all the responses as I continye to refine the code.  Starting with the include directive.  The source is getting too big to manage, so yes, Im definitely in agreement there.

BigDumbDinosaur

Quote from: xlar54Excellent suggestions - too many to comment on at once, but i really appreciate the feedback.  I continue to learn more and more regarding ML development, and the feedback really helps.  Sometimes I think just jumping into a larger project head-first is not at all a bad thing.  Sticking with subroutines, and trying to keep things relatively modular.  Occasionally you run into a hard to find bug, like the ADC issue nikoniko helped me with earlier..those are little kicks in the hiney to pay attention to opcode details.  Being an ML programmer back in the day would have been quite an interesting and educational job.  Ill go over all the responses as I continye to refine the code.  Starting with the include directive.  The source is getting too big to manage, so yes, Im definitely in agreement there.
I started writing 6502 machine language in the late 1970s, and in the late 1980s, wrote a truck leasing and billing system designed to run on C-128Ds multiplexed to a Lt. Kernal hard disk subsystem.  Since the package had to include ISAM database management (no such native function existed in the 8 bit Commodore world), the entire package had to be developed in 8502 assembly language -- there was no BASIC anywhere.  The complete package contained nearly 100,000 lines of machine code.  Needless to say, the whole shebang was modularized, as to not do so would have made it far too unwieldy to manage.

You could take a page from the world of ANSI C programming, where each function (aka subroutine) is created in a separate source file and then all are linked during compile time.  In 65xx M/L, you can do the same thing if your assembler supports such functionality (most do).  For example, the code that watches the keyboard and decides what has to be done based upon the user's actions would be in one source file, the code that manipulates the display in another source file, code that saves a file to disk in yet another, etc.  Your basic definitions (e.g., kernal ROM, ZP locations, etc.) should be in a file that is read at the start of assembly.

Modular development has some important advantages.  The first is that tracking a bug to a particular subroutine can be easier -- you're working with a small file, not a 10,000 line monster.  Secondly, it's easy to replace one version of a subroutine with another -- merely create the new version in a new source file and change the filename reference in the includes list in the primary source file.  Reassemble and voila!  Your new sub is ready for use.

Externalizing manifest constants, memory locations and operating system entry points means you only define these things once, not each time you write new code (you can reuse your includes, of course).  Most importantly. don't embed hard coded values into any program.  If you do that, you make it difficult to change such references, as you are forced to go through every file and track down the reference by numeric value.  If, instead, you externally define a location or constant with a label and religiously use that label reference in all source files, you only need to change the label definition and it will automatically take care of everything at assembly time.  This is *very* important in a project of the size that your editor will become.

By the way, although I do little Commodore development anymore (no hardware available), I still have the "include" files that I created years ago when I started writing C-64 assembly language routines.  I also have a comprehensive set for the 128.  If I can ever figure out how to upload them to this site I may do so.

Something that I did do a few years ago was rework a old program I developed in the mid-1980's that makes the C-128's user port behave like a Centronics-compatible parallel printer port, porting it to the C-64.  I may eventually upload both versions as examples of modular assembly language development.
x86?  We ain't got no x86.  We don't need no stinking x86!

nikoniko

Quote from: bigdumbdinosaurSomething that I did do a few years ago was rework a old program I developed in the mid-1980's that makes the C-128's user port behave like a Centronics-compatible parallel printer port, porting it to the C-64.  I may eventually upload both versions as examples of modular assembly language development.
Sounds like a neat project. What did you use it for?

xlar54

yeah, if you could somehow get the reference file converted over, that would be great.  I think this project is starting to develop one, and Im pulling some decent routines down too from various sources (VDC access, etc).  Im with you on the C-style of development.. keep things modular, and clean up after yourself.  One question - is it common to stash away A,X, & Y at the beginning of a routine, and then PLA them back when done?  Except of course for those routines which return a value.  One of my early bugs in coding in ML always had to do with forgetting that a register was being affected.  Pushing them onto the stack and then recovering them seems to make sense to me to avoid bugs like that.

BigDumbDinosaur

Quote from: nikoniko
Quote from: bigdumbdinosaurSomething that I did do a few years ago was rework a old program I developed in the mid-1980's that makes the C-128's user port behave like a Centronics-compatible parallel printer port, porting it to the C-64.  I may eventually upload both versions as examples of modular assembly language development.
Sounds like a neat project. What did you use it for?
At the time when I developed it, I had a Star Micronics SR10 printer attached to the serial port through a CardCo interface.  I acquired an Epson-compatible daisy wheel printer, which I also wanted to attach to the C-128 to print reports, business letters, and so forth.  Trouble was that I didn't have another CardCo interface and needed to get the daisy wheel unit going ASAP.

So I spent one evening studying how the user port worked and then scoped some of the lines to see how they behaved when different bit patterns were written into the CIA.  From that, I figured out how to make the user port pretend to be a Centronics port.  Once I had worked out the bus protocol, the rest was an exercise in wedging a driver into the 128's kernel and concocting a PETSCII-ASCII translation function (easy in M/L).

Much to my amazement, the code worked on the first try -- how often does that happen in M/L?  I later added support for some Commodore-specific escapes (e.g., cursor down, which switches the printer from upper case mode to mixed case mode), different secondary addresses (e.g., SA 7, which defaults the printer to mixed case mode), and configurable device numbers.  The latter made the driver transparent to BASIC and any M/L that stuck to the CBM kernal jump table.

As it turned out, the hardware interface I developed for making the physical connection between the C-128 and the printer would work with SuperScript 128, since the latter had a built-in user port driver that talked to CIA #2 in the same way my program did.  I used that setup for a long time, later on extending it to use part of RAM1 as a buffer and generating the actual output via an IRQ wedge.
x86?  We ain't got no x86.  We don't need no stinking x86!

BigDumbDinosaur

Quote from: xlar54One question - is it common to stash away A,X, & Y at the beginning of a routine, and then PLA them back when done?  Except of course for those routines which return a value.  One of my early bugs in coding in ML always had to do with forgetting that a register was being affected.  Pushing them onto the stack and then recovering them seems to make sense to me to avoid bugs like that.
In general, the lower level I go, the more I tend to preserve registers.  The only caveat is that stack acrobatics take time and if a low level sub that is called a lot pushes and pulls registers, the extra execution time could become an issue.  This is where top-down design can help, as it can identify the most often-used functions and how critical it is for those functions to preserve registers.  There's no one answer.
x86?  We ain't got no x86.  We don't need no stinking x86!

airship

You've got to watch stack size too, as you've only got 256 bytes to work with.
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

BigDumbDinosaur

Quote from: airshipYou've got to watch stack size too, as you've only got 256 bytes to work with.
True enough, although it's a rare situation in M/L where the stack gets wrapped (I've never succeeded in doing so).  Each subroutine call pushes two bytes and a complete register save is four bytes.  That's six bytes pushed per call.  INT(256/6) = 42, so you can go pretty deep without exhausting stack space.  Obviously, if a program is getting that complicated, some planning is warranted -- shooting from the hip will probably result in a lot of reset button pushing.  :P  Also, the programmer has to be aware of stack acrobatics in the IRQ and NMI handlers, kernal calls, etc.

Now, in BASIC, it's another matter.  FOR-NEXT and DO loops consume stack space the way politicians use up tax dollars.  That's why the 128's BASIC 7.0 was given a 512 byte runtime stack (which I have often used as a disk block buffer in Lt. Kernal development).  BASIC 7.0 would choke if it had been forced to use the hardware stack the way BASIC 2.0 does in the older machines.
x86?  We ain't got no x86.  We don't need no stinking x86!

xlar54

Updated version of the code on the site.  Using the Transactor's code for saving and restoring the 80 col screen, Ive now added some basic drop down menus on the editor. They're actually quite fast and responsive. Use the Commodore key and F for the file menu, and C= E for the edit menu (only ones implemented so far). I still havent been able to figure out 6502TAS's issue with the include directive so for now its still one single file growing pretty quick.  As development continues, Ill be refactoring some items to get rid of code that is redundant. 

nikoniko

Before things get too unmanageable, check out 64TASS: http://singularcrew.hu/64tass/

Syntax is the same as the assembler you're using, but .include works just fine. (Just tested it myself.)

BigDumbDinosaur

What's the status on this project?
x86?  We ain't got no x86.  We don't need no stinking x86!

xlar54

Quote from: BigDumbDinosaur on April 29, 2008, 02:47 PM
What's the status on this project?

Unfortunately Ive had some issues which have taken me away from coding for awhile.  Hope to get back to things soon though.  Anyone who might like to poke at this though is more than welcome.  Id love to see it moved on into a more functional state.

airship

*bump* because this is a project I would really like to see completed.
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine