Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by rje

  1. Ha! I had completely forgotten about the computer vision assignments that used text-bitmaps to trace edges, sharpen, compute moments, determine shape, etc. Thanks for the reminder.
  2. And here's the corresponding script to deconstruct a sprite into a pixobject. use strict; # # Convert CX16 sprites to pixobjects # USAGE: perl bin2pix.pl <4bpp | 8bpp> <width> <sprite file> # =pod The VERA sprite format has two varieties: 4 bits per pixel 8 bits per pixel This script is not smart enough to know the color depth; therefore, the command line has to specify "4bpp" or "8bpp". Similarly, the script is not smart enough to know the image width. The output is a pixobject with this format: =NAME 4bpp|8bpp WIDTHxHEIGHT "NAME" is the filename minus the extension. WIDTH and HEIGHT are in pixels. What follows is the lines of data. These values are supported: 0-9 colors 0-9 A-Z colors 10-35 a-z colors 36-61 =cut my $bpp = shift || die "USAGE: $0 <4bpp | 8bpp> <width> <sprite file>\n"; my $width = shift || die "USAGE: $0 $bpp <width> <sprite file>\n"; my $infile = shift || die "USAGE: $0 $bpp $width <sprite file>\n"; my ($name) = $infile =~ /^(\w+)\./; my $line = ''; my @data = (); open my $in, '<:raw', $infile || die "ERROR: cannot open sprite file [$infile]\n"; read $in, my $nullbytes, 2; while (read($in, my $c, 1) != 0) { my $v = unpack 'C', $c; if ($bpp eq '4bpp') # two chars per byte { $line .= decodeByte($v >> 4); $line .= decodeByte($v & 0x0f); } else # one byte per byte { $line .= decodeByte($v); } if (length($line) == $width) { push @data, $line; $line = ''; } } close $in; my $height = scalar @data; my $dimensions = $width . 'x' . $height; my $data = join "\n", @data; print<<EOPIXOBJ; =$name $bpp $dimensions $data EOPIXOBJ sub decodeByte { my $val = shift; return '.' if $val == 0; return '*' if $val == 1; return chr(48+$val) if $val < 10; # '0'..'9' return chr(55+$val) if $val < 36; # 'A'..'Z' return chr(61+$val) if $val < 61; # 'a'..'z' return '.'; # everything else is a dot }
  3. Back in my first job, we represented raster images in a text format we called "pixobjects". Our data was monochrome, so an asterisk represented an "on" pixel, and a space or dot was "off". So a file representing a house would look sort of like this: =house1 4bpp 16x8 ****** ********** ************** ********** *** ** *** ****** *** I've embellished the header line, which used to only contain the name of the object and (optionally) its dimensions, to include bits per pixel, because I feel the need for this sort of command line tool again. ALTHOUGH I remember that someone already WROTE a REAL sprite editor. Nevertheless, I dashed out a Perl script that would handle 4bpp images in this textual format. It can also handle a subset of 8bpp -- values 0 through 61. use strict; # # Convert pixobjects to CX16 sprites # USAGE: perl pix2bin.pl <input file> # =pod The pixobject file format is as follows: A header line, containing the object's name, its bits per pixel, and its dimensions. Data lines following. Hexadecimal digits are interpreted properly. Values from 0 up to 61 are handled as 0-9, A-Z, and a-z. The full 8 bit range of values are not handled correctly. Example: =house1 4bpp 16x8 ****** ********** ************** ********** *** ** *** ****** *** This should be useful for creating small 4bpp sprites for the CX16. I naturally want to write a complementary utility that will unpack a CX16 4bpp sprite into a pixobject file. =cut my $infile = shift || die "SYNOPSIS: $0 pixobj\n"; open my $in, '<', $infile; my $header = <$in>; my @data = <$in>; close $in; chomp @data; my ($name, $bpp, $width, $height) = $header =~ /=(\w+) (4bpp|8bpp)\s+(\d\d?)x(\d\d?)/; die "Cannot read header\n" unless $bpp && $width && $height; my $dimensions = $3 . 'x' . $4; my $outfile = sprintf("%s_%s_%s.bin", $bpp, $name, $dimensions); print "===> $outfile <=== $bpp, $width x $height\n"; open my $out, '>', $outfile; print $out pack 'xx'; # initial two null bytes foreach my $line (@data) { my @bits = split '', $line; my @line = (); my $odd = 1; # This is how we'll handle 4 bits per pixel. my $value = 0; for (0..$width-1) { my $charval = getCharval($bits[$_]); if ($bpp =~ /4bpp/i && $odd) { $value = $charval << 4; # initial value = upper nybble } else # 8 bits OR we're even { $value += $charval; print $out pack 'C', $value; $value = 0; # and reset } $odd = 1 - $odd; # toggle for nybbles } } close $out; sub getCharval { my $c = shift; return ord($c)-48 if $c =~ /\d/; # 0-9 return ord($c)-55 if $c =~ /[A-Z]/; # 10-35 return ord($c)-64 if $c =~ /[a-z]/; # 35-61 return 1 if $c eq '*'; return 0; # everything else is a zero }
  4. ADSR data, I had earlier thought, could be four bytes per voice, for a total of 64 bytes, buried somewhere around $400 or so. It would also need a status counter for each voice, so that we'd know where the sound was along its envelope. The status would have to be, I think, a quantized (sic?) "unit" segment, with 0 meaning the sound is right at the start of the Attack phase, and MAX_VALUE meaning the sound has just finished the Release phase. OR I just do things a brute force way and not try to be clever about it.
  5. Here's my initial structure. The initial function would therefore be this: void defineVoice( Voice* voice ); ----- typedef struct { int frequency: 16; int channel: 2; int volume: 6; int waveform: 2; int pulseWidth: 6; int attack: 8; int decay: 8; int sustain: 8; int release: 8; } Voice; Then I've got things like these: #define CHANNEL_LEFT (1 << 6) #define CHANNEL_RIGHT (2 << 6) #define CHANNEL_BOTH (3 << 6) #define WAVE_PULSE (0 << 6) #define WAVE_SAWTOOTH (1 << 6) #define WAVE_TRIANGLE (2 << 6) #define WAVE_NOISE (3 << 6)
  6. I'm slowly building a kind of C toolchain so I can program interesting things on the CX16. Now it's time to start thinking about a PSG interface. With sprites, I've had success by defining a sprite record, then writing functions that do basic sprite things. I think that is how I will approach the PSG as well. I've muttered about a general PSG API in other discussions on this forum, so I'll probably dig through them to figure out where to start. Obviously, a voice definition struct would have the voice number, waveform, volume. Would it have EVERYTHING, including frequency? Then the functions just take what they need from the struct pointer? My thought is I don't want to multiply typedefs. 1. Does it make sense to have most or all data in one typedef? 2. Does it make sense to have ADSR data in there as well? 3. Does it make sense to try to implement envelope control (in C)? I suppose there will have to be an external "clock" that calls a C envelope manager, which means there will have to be one sound structure allocated per voice. And yes, that sound "structure" is probably just going to be a hunk of contiguous memory that an interrupt-driven assembly routine could also handily work in instead. 4. What else am I missing?
  7. Very true. That's a good option. But I'll have to stew on it for awhile. However, I think before I get to that point, I want to write a simple PSG interface in C.
  8. I've started thinking about how I'm going to interact with the map. The ship should "stop" when it hits land, and a landing party should take over movement. The ship's movement needs to be finer grained. (easy) The landing party should interact with settlements. If I have enough VERA RAM left, I want sprites to define a few key buildings (maybe 16 x 16?). In this way, the landing party can interact with the town without requiring a separate town map, and it keeps essential things without adding stuff I don't want to do. Replacing the "settlement" sprites gives me 8K VERA RAM to define buildings and groups of people. I can re-use the settlement graphics and chop them up into some of these smaller, 16 x 16 sprites.
  9. So. Yeah. I'll have to revert to the old school method of using shoreline sprites. I figure one 8 x 64 "vertical shoreline", one 64 x 8 "horizontal shoreline" (I can flip them based on what I need), and a 16 x 16 "inner corner adapter" (which I can flip on either axis as needed). I'll sacrifice one of the terrain sprites (4K) for this. 512b: Horiz Shoreline 512b: Vert Shoreline 256b: Corner Also, I can stop overlapping the sprites. Current overlap is 8 pixels on every side, which means my 8 x 8 grid covers 384 x 384 pixels. I can now move down to a much more reasonable 6 x 6 sprites, stay at 384 x 384 pixels, but have more interesting terrain. AND this gives me room for a couple of badly-needed tactical sprites.
  10. Good idea, thank you. I'm also thinking about using characters for the ocean background instead of sprites. I guess that means I'd have to scroll a character layer too. Going to characters for the background "ocean" also has the benefit of not overlapping the terrain sprites (I overlap because each terrain sprite is, by itself, essentially an "island" with a jaggy "coastline"). *** UPDATE. VSYNC! Can C code do that? Ohhhh, I'd write an assembly interrupt. OK.
  11. I should be able to get away with simple, blocky sprites, similar to Pirates:
  12. Yep... I was thinking 8 x 64 and 64 x 8 too, but yep. The sprite block swapping is causing a bit of flashing effects. I'll think about that too... I might have to swap out sprite definitions off-screen. Don't want to go as far as double buffering, but...
  13. Okay with that minor crisis averted, now I can do some real work. My map is 8 x 8 terrain sprites, and I'm going to scroll them. I THINK what I'll have to do is: * put the map-sprites behind the default text layer * draw a thick "frame" slightly overlapping the map using reverse-spaces Now the tricky part. * When the player moves, the SCROLL X/Y OFFSET is changed, NOT the actual "position" * When the X OFFSET < -24, Then the x position is decremented and the x offset is changed to +24. * When the X OFFSET > 24, Then the x position is incremented and the x offset is changed to -24. * Similarly for the Y OFFSET. "Position" tells us what terrain to draw in each tile. "Offset" tells us what screen position to draw that tile. ...something like that anyway.
  14. If I ever go off the deep end and actually become a true tinkerer I'd bid on something like this and, perhaps, gut it -- assuming it has multiple problems (mainboard, power supply, keyboard, monitor, who knows). The case is in good shape. https://www.ebay.com/itm/115005318802?hash=item1ac6d9e692:g:K7MAAOSwTPNhS9Td Or, more perversely, a TRS-80 Model 4 case... $75... https://www.ebay.com/itm/194385725279?hash=item2d424ac75f:g:AnYAAOSwqp5hTJDi
  15. So in other words, I'm better off figuring out how to get an X16 and its monitor to fit into a PET frame. XD
  16. OMG I CAN'T DO BINARY MATH. ...Told you I was doing something stupid...
  17. Found it. Shifting the palette by -4 fixes these images. WHY.... why why why why.... I'm going back to the BASIC code to see if there's a palette shift there...
  18. Well this is interesting. None of the 32 x 32 sprites show any palette shift; ALL of the 64 x 64 sprites show it... in C, but not in BASIC.
  19. It gets even funner. I just did a fresh copy of all these images back into the /basic folder, and re-ran the BASIC program... just to confirm it's loading the same files... and the mountain is CORRECT there. Hilarious. So now I look at what BASIC is doing, versus what C is doing. BASIC is loading those images into RAM banks, and then the data in those banks is being copied into VRAM. There's a VLOAD command in BASIC, isn't there? Can I vload straight to VERA $4000 in BASIC? ==> YES, yes I can, and it works great (when loading from the host file system).
  20. LOL. Here's the correct image, from the BASIC program, next to the image loaded from the C code: Obviously a palette shift of some kind. The transparency layer looks like it's dark blue. The "grass" is cyan-ish. The mountain shadow is a darker cyan-like thing. The mountain proper is brown, with a lot of chaos where there are shades of white-gray in the correct image.
  21. Plainly I'm doing something wrong with my 64 x 64 sprites -- which are 8BPP after all. So I'm going to approach this the same way I approached the 32 x 32 sprites: slow and steady. #define TO_RAM 0 #define TO_VERA 2 #define SPRITE_REGISTERS(spritenum) ((spritenum << 3) + 0xfc00) // sprite blocks are in 32 byte chunks. #define SPRITE_BLOCK(addr) (addr >> 5) #define SPRITE_MODE_8BPP 128 #define SPRITE_32_BY_32 (128 + 32) #define SPRITE_64_BY_64 (196 + 48) #define SPRITE_LAYER_BACKGROUND (1 << 2) #define SPRITE_LAYER_0 (2 << 2) #define SPRITE_LAYER_1 (3 << 2) int block = SPRITE_BLOCK(0x4000); int mode = SPRITE_MODE_8BPP; int x = 100; int y = 100; int z = SPRITE_LAYER_1; int dimensions = SPRITE_64_BY_64; int palette_offset = 0; // //. load the sprite into VRAM // cbm_k_setnam("terrain/island-mountain-x.bin"); // cbm_k_setlfs(0,8,0); // cbm_k_load(TO_VERA, 0x4000); // this all works // // set port 0 address and increment // VERA.control = 0; // port 0 VERA.address = SPRITE_REGISTERS(1); // sprite 1 VERA.address_hi = VERA_INC_1 + 1; // the "+1" is the VRAM high address bit. vera_sprites_enable(1); // in cx16.h VERA.data0 = block & 0xff; // lower VRAM address bits VERA.data0 = mode + ((block >> 8 ) & 0x1f); VERA.data0 = x & 0xff; VERA.data0 = x >> 8; VERA.data0 = y & 0xff; VERA.data0 = y >> 8; VERA.data0 = z; // leave collision mask and flips alone for now. VERA.data0 = dimensions + palette_offset;
  22. That's absolutely correct. I think he decided when he laid out his original plans. The design only changed when it gets blocked. In other words, he's not dithering.
  23. Understood. I'm currently using the native file system, so I'm ok. I'll probably get bit if I try to move to an SD card before migrating to R39 tho.
  • Create New...

Important Information

Please review our Terms of Use