Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(1 edit) (+1)

Here's another program, though really it's more of a drawing routine for programs to use, with a demonstration drawing.


(I didn't care to do the win screen again, and this better shows off some of what it can do, such as the dotted/multicolored lines, and the wrapping trick.)

It doesn't itself use overlays, but is small enough to fit within a single overlay, and could easily be adjusted to take the drawing as a parameter instead of hardcoding it. (This would actually make the program smaller.)

As such, it should work with any basic bootloader - though I discovered that the built-in bootloader starts running the program at address 4 instead of 0, which doesn't work for this program. (I could make it work, but haven't bothered to.)

In some ways, this is a line drawing routine, as it can easily draw several common types of lines (though not all of them) - but really, it'd be more accurate to call it a repeated-pixel placer.

The routine is given the address of a drawing on disk, which consists of a list of words that each describes a "line" to draw. It draws each in turn, until it hits a list terminator.

Each word of that list consists of the start pixel (where to start drawing), the stop pixel (where to stop drawing), and an increment (how much to change the position by for each step). The list terminator is a word where the start pixel is equal to the stop pixel. (It is worth noting that the stop pixel is never drawn, while the start pixel is - and that the stop pixel must be exact, that is, a position that would be drawn to if it wasn't the stop pixel.)

It's fairly fast - 3 cycles per pixel drawn (2 if IFJMP is instant), plus some setup for each word and for the routine as a whole. (I calculated that it spends 496 cycles to draw just the background and window frame, which in the demo is just the first 5 words of the drawing.)

Note that the code given below, while ready to be pasted into Senbir, is actually the output I got from my preprocessor when given the original source code - that source may be easier to read.

// Draws lines by repeatedly adding an increment to a pixel.
// Overlay main started at address 0
DATAC 00000000000000000000000000010000 // End address for overlay
// Assumed: R15 = 1
MOVI 5 15 // R5 = ram[..] : load address of data to be drawn
MATH 4 4 1 // R4 = 0 : initialization for updating only parts of it
MATH 3 3 1 // R3 = 0 : initialization for updating only parts of it
MATH 2 2 1 // R2 = 0 : initialization for updating only parts of it
// Label next found at address 5
GETDATA 1 3 5 // R1 = read(disk, R5) : load next step
MATH 15 5 0 // R5++ : update address
PMOV 1 2 0 8 0 0 // R2[0:8] = R1[ 0: 8] : set start pixel
PMOV 1 3 9 17 9 0 // R3[0:8] = R1[ 9:17] : set end pixel
IFJMP 1 14 0 // jump to done if R2 == R3
PMOV 1 4 18 26 18 0 // R4[0:8] = R1[18:26] : set position increment
// Label loop found at address 11
SETDATA 0 3 2 // Draw a pixel from R2
MATH 4 2 0 // R2 += R4 : increment position
IFJMP 1 10 1 // jump to loop if R2 != R3
JMP 1 4 // jump to next otherwise
//
// Label done found at address 15
HLT
//
// Label data_addr found at address 16
DATAC 00000000000000000000000000010001
//
// Overlay main ended at address 16
//
// Label demo_data found at address 17
// Start pixel (is drawn), stop pixel (is not drawn), position increment
DATAC 10000100110111011100000000100000 // Main area fill, TL-to-BR
DATAC 01111011100111111111111100000000 // Bottom row, R-to-L
DATAC 01000011000111111111111111100000 // Left column, B-to-T
DATAC 01000100010000000000000100000000 // Top row, L-to-R
DATAC 01111100110000000000000000100000 // Right column, T-to-B
//
DATAC 00000101000010111000000100100000 // Black \ line
DATAC 11000110111010100100000011100000 // Light-green / line
//
DATAC 00010100100111100100001000000000 // Dotted line 1: 2px
DATAC 00010101001000101000001100000000 // Dotted line 2: 3px
DATAC 00010101101000101100010000000000 // Dotted line 3: 4px
//
DATAC 00010110100111110100001000000000 // 2-color line part 1
DATAC 01011010110000010100001000000000 // 2-color line part 2
//
DATAC 00010111001000111000001100000000 // 3-color line part 1
DATAC 11011011011111111000001100000000 // 3-color line part 2
DATAC 01011111010000011000001100000000 // 3-color line part 3
//
DATAC 00000000000000000000000000000000 // End of list

(Feel free to use this code for your own purposes, same as with any other Senbir assembly code I post in this forum - I wouldn't have posted it if I minded.)

Reminds me of a simplified vector graphics renderer of some sort.  The speediness sounds really handy for, say, game development - every cycle counts when you're rendering a game, even in 2d!  IFJMP is instant now (fixed that bug), so, yes, it takes 2 cycles per pixel, which is...frankly, I didn't even know you could optimize the code that much.  Very impressive!


Your preprocessor is really cool.  The base source file for the line-renderer/pixel-pusher looks like real Assembly, like the sort I often see in, say, online 6502 development tutorials.  It's quite a bit easier to understand, and omg the use of underscores in the binary sections is a huge improvement to readability.

The only major comment I have is that it might be worth adding the estimated/intended address of every word in the comments for that line - makes it easier to debug when something (inevitably) goes catastrophically wrong.  Combined with the comments for, say, "label here", or "overlay here", it should be fairly easy to figure out where the problem is in the original pre-preprocessing source file.  One of the latest updates (dunno if it's the one I released last, or the one that's WIP w/ the code for the cycle timer & such) has a built-in way to check the current address in the Debug screen, meaning that being able to search for said address in your code can help you find what blew up faster than manually trawling through the RAM viewer, looking for the highlighted entry.


(Also, my apologies for taking so long to reply to this.  Internet blew up.  Again.  I feel like such an unprofessional developer >.<)

(1 edit)

Yeah, it reminded me of vector graphics too. I considered calling it something like that, but it kind of felt like it made it sound like it did more than it does, so...

As for the speed, well, it's a tradeoff really - it requires some setup, and using several registers (and the right ones), so it's not ideal for all cases - e.g. if you had lots of individual pixels or very short lines instead of long ones. (It's kind of telling that my Snake game only uses it for drawing the initial window, not during gameplay.)


Re: preprocessor, thanks. It's been a while since I did any assembly other than toys (not that I ever did much with it at all), so I don't really know what the state of the art is there (haven't looked at 6502 tutorials or anything), so it's encouraging to hear that I'm close anyway.

About the underscores and readability - yeah, that's exactly why I added that. I started with just plain 0b syntax for explicit binary numbers (along with 0x for hex), then added the "..." to make the monitor device easier to work with - but many things were still such a chore, what with having to count out the bits every time, so I was thinking some way of grouping/separating the bits would be nice, and... yeah, it was just so much better. I'm not removing that, no way, no how.

I've kept working on it and adding features I wanted, fixing bugs, etc. - I think I know two ways to fix the line number issue (not quite sure which I'll go for, one requires more refactoring than the other). Just need to get around to it. Another thing I've been meaning to do is include not just the disk address of labels, but also the local address (where it will be in RAM), since that can be more useful.

I'm not sure if having it add the address on every line will be all that useful to me personally, since I don't usually look all that much at the output (when not working on the preprocessor itself anyway), but I could probably make it do that pretty easily, so if others would find it useful, sure, I can add that. (Who knows, I might find it useful myself once it's there.) It wouldn't be an estimate either, since it needs to track the addresses accurately anyway. And once I fix the line number issue, including that too might also help?

EDIT: OK, I've now fixed the line number bug (went with the easier-to-do way for now), and added the local address to the label comments. I've also added an option to add the address of every instruction in a comment on that line. It can actually add any or all of the original line number, the disk address, and the local/memory address (where it will be when that overlay is loaded), depending on the options given to it, so whichever combination you prefer should be available. Use --help for the details.


No worries on taking a while to reply, I'm pretty slow at that myself, and I understand that life comes first. Also, I do programming professionally, and let me tell you, when the Internet goes down? A lot of things just... stop. Even if you already have all the code you're working on locally instead of being in the middle of code review or similar, "Hm, let me just look that up... Oh wait. *facepalm*"