Vim: Always Learning

Vi (or Vim in it’s modern incarnation) is a text editor. It’s a modal text editor. You can switch from insert mode to normal mode. Insert mode is where you type text into your document, and normal mode is where you type commands that do all sorts of functions. It’s incredibly powerful, incredibly complicated and even exiting the editor is not obvious. 🙂

So it’s always great when I dive into learning something new about Vim. There’s always something new to learn about Vim, even after more than twenty years of using it. I think there should be a Duolingo for Vim.

So, when you’re watched that, and watched it again, and again to understand it, have a look at the following presentation showing off the power of Vim without plugins.

When you just want to use Vim faster …

The power of Vim is in the commands. That also means it requires practice to learn those commands because they’re usually keyboard shortcuts. There’s no Edit menu to remind you to use something you just learned. With just a few core concepts and memorized keys or functions you can be very efficient at moving around or manipulating text.

It’s always Vi

Things I learned recently:

  • :set number relativenumber from the last video and this blog post.
  • :sfind is a game changer. I set the base path to my public_html and sfind will open a file in a new split without figuring out the path.
  • I need to install ctags.

And so much more ..

The Commodore 64 Disk Masher (C64 DMS)

DMSREADER and DMSWRITER are two parts of a Commodore 64 tool I wrote way back in 1994. I presume I got the name from the Amiga DMS tool but I really don’t know. The purpose of the tool was to create an image of 5 1/4 disks but break the image up into smaller DMS files.

Download dms64-1.0.zip.

According to the attached note, I wrote it to upload C64 demos to local BBSes but it was also used by me and the few people I still swapped C64 disks with back then. Were D64 image files around in 1994? If they were I didn’t know about them. They could only be useful to users of higher capacity disks anyway. Don’t bother trying to send a message to the email address in the note. It’s an address I had in first year in college and is long gone!

I’m posting this here because there’s no sign of this tool at all online. Some of my demos are already on CSDB but not this. It’s obviously not that useful these days when a D64 image file is only 171KB but for historical reasons it’s probably of interest. Each of the two parts are a mixture of BASIC and assembly language. By the looks of things I used some sort of illegal character trick to hide the BASIC loader but you can view it easily in CBMXfer.

DMSWRITER recreates the disk from the DMS files. It’s fairly easy to use. Enter your drive number(s) and follow the instructions. Well, almost.

Where I wrote “destination disk” in the DMSWRITER it should have been “source disk” instead. Ooops. When data is written to the destination disk the border flashes with thick multicolour bars but when it flashes quickly with a grey/blue flicker you need to change the source disk to feed it more DMS files.
I noticed that the source drive light continues to flicker after the process is over but sending a reset or loading the disk directory will sort that out. I also noticed the text of the on screen display flashes up while writing as if that data is written to disk. I need to make sure that isn’t the case or we’ll end up with corrupted disks. I never noticed any problems previously so hopefully it’s a false alarm.

DMSREADER is responsible for creating DMS files. The process is much the same, only in reverse. The source drive should contain the disk you want to make an image of and the destination will be a blank disk. You’ll have to use two destination disks unfortunately as I never got around to compressing the output files. There’s still a bug in the READER where it asks you to flip the destination disk a second time, ignore that and just hit space.
Unfortunately when I first tested the reader I received a “?SYNTAX ERROR IN 1000” error just after I was told to flip the destination disk. Fixing it was easy, replacing the following code:

1000 IF (PEEK ($DC01)AND $10)< > 0THEN GOTO 1000
1000 IF PEEK (56321)< > 239THEN 1000

When I wrote that BASIC programme I was too smart by half. To stop people looking at the code I added REM commands followed by an illegal character. Just so I could debug the programme itself I had to edit the RAM where it lived and replace that character. Look for 8F CC in the screenshot below of the Action Replay machine code monitor. I replaced CC with 34 (character “4”) to get a code listing.

The DMSWRITER BASIC loader uses 56321 which is correct, but I have to wonder why I didn’t use a BASIC command to wait for a key press rather than fooling around with low level registers. The original file has been renamed DMSREADER.ORIG.

While looking at the ASM code in the file DMSREAD I think I found a small bug in the otherwise excellent CBMXfer. The first screenshot below is from CBMXfer and you’ll notice that the BNE returns to the wrong address. I loaded an Action Replay cartridge and used the monitor in that to view the same code in Vice where the BNE address is correct. Gave me a fright to think there was such an obvious bug in my code…

Oh yes, make sure you disable any fastloaders and enable true drive emulation in Vice when using these tools.

So totally chuffed that I can use an Action Replay in Vice. I did all my development in that monitor. Yes, not easy …

DMSREADER (BASIC listing)

2 A= 53280
  IF PEEK (52769)= 216OR LO= 1THEN GOTO 4
3 LO= 1
  LOAD "DMSREAD.ASM",8,1
4 POKE A,0
  POKE A+ 1,0
  PRINT CHR$ (8)
  PRINT "{CLR}{WHITE}   DMS 64 READER BY XEER/OZONE"
  REM 4
5 INPUT "SOURCE DEVICE NUMBER(RETURN FOR 8)";Z
6 INPUT "DEST DEVICE NUMBER(RETURN FOR 8)";X
7 IF Z= 0THEN  Z= 8
8 IF X= 0THEN  X= 8
9 SYS 4096
  POKE 52739,X
10 PRINT "{CLR}"
   SYS 52992
   OPEN 15,Z,15
   OPEN 5,Z,5,"#"
20 READ A,B,C
   IF A= - 1THEN GOTO 50
25 TR= A
30 PRINT# 15,"U1";5;0;TR;D
35 SYS 53008
   PRINT "{HOME}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}TRACK     ";"{LEFT}{LEFT}{LEFT}{LEFT}";TR,"SECTOR     ";"{LEFT}{LEFT}{LEFT}{LEFT}";D
40 D= D+ 1
   IF D= CTHEN TR= TR+ 1
   D= 0
45 IF TR= (B+ 1)THEN D= 0
   GOTO 60
46 GOTO 30
50 CLOSE 5
   CLOSE 15
   END 
60 CLOSE 5
   CLOSE 15
100 IF  TR< 33 THEN  GOTO  110
101 PRINT "FLIP DEST DISK!!!"
    GOSUB 1000
110 IF  Z= XTHEN PRINT "{HOME}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}  INSERT DEST DISK AND PRESS SPACE"
    GOSUB 1000
130 POKE 52800,NAME+ 65
    SYS 52736
140 IF Z= XTHEN  PRINT  "{UP}    INSERT SOURCE DISK AND PRESS SPACE"
    GOSUB 1000
150 NA= NA+ 1
    GOTO 10
200 DATA  1,3,21,4,6,21,7,9,21,10,12,21,13,15,21,16,17,21
210 DATA  18,19,19,20,22,19,23,24,19
220 DATA  25,27,18,28,30,18
230 DATA  31,33,17,34,35,17
240 DATA  -1,-1,-1
1000 IF (PEEK (56321))< > 239THEN GOTO 1000
1010 RETURN 

DMSWRITER (BASIC listing)

7 IF PEEK (52757)= 213THEN  LO= 4
8 IF LO= 4THEN GOTO 10
9 LO= 4
  LOAD "DMSWRITE.ASM",8,1
10 POKE 53280,0
   POKE 53281,0
   PRINT "{CLR}{WHITE}DMS 64 WRITE BY XEER/OZONE"
   SYS 4096
11 INPUT "SOURCE DEVICE NUMBER(RETURN FOR 8)";Z
12 INPUT "DEST DEVICE NUMBER(RETURN FOR 8)";X
13 PRINT "INSERT SOURCE DISK AND PRESS SPACE"
   GOSUB 3000
   IF Z= 0THEN Z= 8
14 IF X= 0THEN X= 8
15 NAME= 0
   D= - 1
   POKE 52739,Z
   GOSUB 1000
   POKE 52800,NAME+ 65
   SYS 52992
16 PRINT "{CLR}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}IF THE DISK STOPS ON ONE DISK MODE,"
17 PRINT "SWAP DISKS AND PRESS SPACE!"
18 PRINT "IF THE SCREEN FLASHES THEN U MUST USE   SIDE 2 OF THE DESTINATION DISK"
20 OPEN 15,X,15
   OPEN 5,X,5,"#"
   READ A,B,C
   IF A= - 1THEN GOTO 50
25 TR= A
30 PRINT# 15,"U2";5;0;TR;D
31 PRINT# 15,"B-P";5;0
32 SYS 53008
40 D= D+ 1
   IF D= CTHEN TR= TR+ 1
   D= 0
41 IF TR= (B+ 1)THEN D= 0
   GOTO 60
46 GOTO 30
50 PRINT "FINISHED!"
   CLOSE 5
   CLOSE 15
   END 
60 CLOSE 5
   CLOSE 15
100 TR= A
    D= - 1
    Z= 8
    GOSUB 1000
110 NA= NA+ 1
    POKE 52800,NA+ 65
115 IF  PEEK (52800)= 76 THEN  GOSUB 4000
120 GOSUB 1000
    SYS 52992
140 GOTO 20
200 DATA  1,3,21,4,6,21,7,9,21,10,12,21,13,15,21,16,17,21
210 DATA  18,19,19,20,22,19,23,24,19
220 DATA  25,27,18,28,30,18
230 DATA  31,33,17,34,35,17
240 DATA  -1,-1,-1
1000 IF Z= XTHEN GOSUB 3000
1005 SYS 52736
1010 IF Z= XTHEN GOSUB 3000
1020 RETURN 
3000 IF PEEK (56321)< > 239THEN 3000
3005 RETURN 
4000 POKE 53280,PEEK (53280)+ 1
     POKE 53280,PEEK (53280)- 1
4004 IF PEEK (56321)< > 239THEN 4000
4005 RETURN

Simple UTW Performance Boost

The Ultimate Tag Warrior plugin for WordPress is a great plugin. It does the job of handling tags rather well. I’ve noticed many hits from Google searches that go to a tag page instead of a category page of the same name so there’s something there that Google likes. I love it! Christine Davis did a super job filling a niche with the “ultimate” tool that anyone could want.
Unfortunately what isn’t so good is what goes on behind the scenes:

  1. UTW ignores your default category when saving your posts. Why ignore the default category when that is probably the subject you care most about? On my photoblog, In Photos.org my default category is “photos” and I certainly want people to find my images.
  2. UTW does an expensive SQL JOIN when it can’t find any tags for a post. Remember what I said about the default category? If you primarily post to your default category then every time someone sends you a request your server will get bogged down trying to find a non-existant record. It’s really bad when you have several years of posts and thousands of records in there.
  3. If you’re not going to use the fancy AJAX tag search then don’t load the associated Javascript. It saves an extra request on the first page load, and the browser doesn’t have to process the file.

Here is a patch that addresses those issues. It needs to be edited before you use it because you have to add a default tag. Here’s how to install it:

  1. Decide on a useful default tag. For this blog I use “holy-shmoly”. Open your WordPress database with phpmyadmin and look in wp_post2tag (the name may be different depending on your prefix). If you have already used the tag then find it and note down the tag_id of that tag. Otherwise, add your tag and note the new tag_id.
  2. Edit utw-patch.diff and look for the strings DEFAULT_TAG_NAME and DEFAULT_TAG_ID and replace with the name and id you picked out in the last step.
  3. To apply the patch, copy it into your wp-content/plugins/UltimateTagWarrior/ directory and run the following command.

    patch -p0 < utw-patch.diff

  4. If you don’t have access to patch, then open utw-patch.diff in your favourite editor and make as much sense of it as you can. It’s not hard once you have the source files open as well.

I’ve noticed a significant drop in server spikes since I made those changes. It takes a while for the default tag to be populated as it’s done when posts are visited but once it’s finished you should see a marked improvement. Oh, and install WP-Cache 2.0 as well. That’ll help you ride out the occasional digg!

Download the patch: utw-patch.diff for UTW 3.14159 for WordPress 2.0

By request, here are the modified files. Rename .txt to .php and copy them into your plugins/UltimateTagWarrior/ directory, overwrite the originals:

  1. ultimate-tag-warrior-actions-wordpress2.txt
  2. ultimate-tag-warrior-actions.txt
  3. ultimate-tag-warrior-core.txt