base:kick_assembler_macros
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
base:kick_assembler_macros [2015-05-31 21:27] – added the RGB definitions tww | base:kick_assembler_macros [2016-08-10 02:24] (current) – tww_ctr | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Kick Assembler Macros ====== | ||
+ | Every now and then, someone (mostly Slammer), posts a nice Kick Assembler macro on CSDb. This page is intended to collect such tips and tricks that relate specifically to Kick Assembler. Please add your own macros here! | ||
+ | |||
+ | To call a Macro in Kick Assembler, just write something like: | ||
+ | < | ||
+ | ===== Graphics conversion ===== | ||
+ | |||
+ | ==== " | ||
+ | |||
+ | By: TWW/CTR | ||
+ | |||
+ | This macro takes a .PNG file and converts it into a 2x2 tile based bitmap(koala) format. | ||
+ | |||
+ | You need to set the **Palette and the background color** inside the macro (make sure these are correct) and then call the macro with the filname and output location of the data in memory as follows: | ||
+ | |||
+ | < | ||
+ | : | ||
+ | </ | ||
+ | |||
+ | Here follows the Macro: | ||
+ | |||
+ | < | ||
+ | .macro BitmapPack (FileName, | ||
+ | |||
+ | .function RGB_Sum(argument1, | ||
+ | .return argument1 * 65536 + argument2 * 256 + argument3 | ||
+ | } | ||
+ | |||
+ | .var RGB_Index = Hashtable().put( | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($ff, | ||
+ | RGB_Sum($92, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($72, | ||
+ | RGB_Sum($48, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($8a, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | RGB_Sum($00, | ||
+ | ) | ||
+ | |||
+ | .const BG_Color | ||
+ | .const BackgroundColor = RGB_Index.get(BG_Color) | ||
+ | .const BlockSizeX | ||
+ | .const BlockSizeY | ||
+ | .const RawImage | ||
+ | .const ImageBlocksX | ||
+ | .const ImageBlocksY | ||
+ | |||
+ | .var void = 0 // Empty value used to call funtions without any return | ||
+ | .var PixelPosX | ||
+ | .var PixelPosY | ||
+ | .var BlockNumber | ||
+ | .var FinalBMP | ||
+ | .var Final0400 | ||
+ | .var FinalD800 | ||
+ | .var FinalMap | ||
+ | .var DiscoveredBlocks = Hashtable() | ||
+ | |||
+ | .for (n = 0 ; n < ImageBlocksY ; n++) { | ||
+ | .for (j = 0 ; j < ImageBlocksX ; j++) { | ||
+ | .var BlockDataBMP | ||
+ | .var BlockData0400 | ||
+ | .var BlockDataD800 | ||
+ | .var BlockStructure | ||
+ | .for (i = 0 ; i < 4 ; i++) { // Start to decode 1 charblock | ||
+ | .var CharColors | ||
+ | .var CharColors2 | ||
+ | .eval CharColors | ||
+ | .eval CharColors2 | ||
+ | .for (y = 0 ; y < 8 ; y++) { | ||
+ | .var BitMask = 0 | ||
+ | .for (x = 0 ; x < 4 ; x++) { | ||
+ | .var RGB_Value = RawImage.getPixel(PixelPosX+x*2, | ||
+ | .if ([RGB_Value!=BackgroundColor]&& | ||
+ | .eval CharColors.put(RGB_Value, | ||
+ | .eval CharColors2.put(CharColors2.keys().size(), | ||
+ | } | ||
+ | .if(CharColors.get(RGB_Value) == 1) .eval BitMask = BitMask | %10000000>> | ||
+ | .if(CharColors.get(RGB_Value) == 2) .eval BitMask = BitMask | %01000000>> | ||
+ | .if(CharColors.get(RGB_Value) == 3) .eval BitMask = BitMask | %11000000>> | ||
+ | } | ||
+ | .eval BlockDataBMP.add(BitMask) | ||
+ | } | ||
+ | .if (CharColors.keys().size() == 1) { | ||
+ | .eval BlockData0400.add(BG_Color) | ||
+ | .eval BlockDataD800.add(BG_Color|BG_Color<< | ||
+ | } | ||
+ | .if (CharColors.keys().size() == 2) { | ||
+ | .eval BlockData0400.add(RGB_Index.get(CharColors2.get(1))|BG_Color<< | ||
+ | .eval BlockDataD800.add(BG_Color) | ||
+ | } | ||
+ | .if (CharColors.keys().size() == 3) { | ||
+ | .eval BlockData0400.add(RGB_Index.get(CharColors2.get(1))|RGB_Index.get(CharColors2.get(2))<< | ||
+ | .eval BlockDataD800.add(BG_Color) | ||
+ | } | ||
+ | .if (CharColors.keys().size() == 4) { | ||
+ | .eval BlockData0400.add(RGB_Index.get(CharColors2.get(1))|RGB_Index.get(CharColors2.get(2))<< | ||
+ | .eval BlockDataD800.add(RGB_Index.get(CharColors2.get(3))) | ||
+ | } | ||
+ | .if (i==0) { .eval PixelPosX = PixelPosX + 8 } | ||
+ | .if (i==1) { .eval PixelPosX = PixelPosX - 8 .eval PixelPosY = PixelPosY + 8 } | ||
+ | .if (i==2) { .eval PixelPosX = PixelPosX + 8 } | ||
+ | .if (i==3) { .eval PixelPosX = PixelPosX + 8 .eval PixelPosY = PixelPosY - 8 } | ||
+ | } | ||
+ | .eval BlockStructure.add(BlockDataBMP, | ||
+ | .var DoesTheBlockExists = DiscoveredBlocks.get(BlockStructure) | ||
+ | .if (DoesTheBlockExists == null) { | ||
+ | .eval DiscoveredBlocks.put(BlockStructure, | ||
+ | .eval DoesTheBlockExists = BlockNumber | ||
+ | .eval BlockNumber = BlockNumber + 1 | ||
+ | .eval FinalBMP.addAll(BlockDataBMP) | ||
+ | .eval Final0400.addAll(BlockData0400) | ||
+ | .eval FinalD800.addAll(BlockDataD800) | ||
+ | } | ||
+ | .eval FinalMap.add(DoesTheBlockExists) | ||
+ | } | ||
+ | .eval PixelPosX = 0 | ||
+ | .eval PixelPosY = PixelPosY + 16 | ||
+ | } | ||
+ | .pc = Destination "Level 1 - Bitmap Tile Graphics" | ||
+ | .fill FinalBMP.size(), | ||
+ | .pc = * "Level 1 - Tile Screen Colors" | ||
+ | .fill Final0400.size(), | ||
+ | .pc = * "Level 1 - Tile $d800 Colors" | ||
+ | .fill FinalD800.size(), | ||
+ | .pc = * "Level 1 - Map" | ||
+ | .fill FinalMap.size(), | ||
+ | } // end macro | ||
+ | </ | ||
+ | |||
+ | ==== Load Multicolor Koala format straight from .png file ==== | ||
+ | |||
+ | The routine is called as follows: | ||
+ | |||
+ | < | ||
+ | : | ||
+ | </ | ||
+ | |||
+ | |||
+ | First, the palette needs to defined as follows (One could consolidate this into one list like done below in the hires routine): | ||
+ | |||
+ | < | ||
+ | // VICE C64 PALETTE | ||
+ | .struct RGB {r, | ||
+ | .const black = RGB( 0, 0, 0) // #$000000 | ||
+ | .const white = RGB(255, | ||
+ | .const red = RGB(137, 64, 54) // #$894036 | ||
+ | .const cyan = RGB(122, | ||
+ | .const purple | ||
+ | .const green = RGB(104, | ||
+ | .const blue = RGB( 62, 49,162) // #$3E31A2 | ||
+ | .const yellow | ||
+ | .const l_brown = RGB(144, 95, 37) // #$905F25 | ||
+ | .const d_brown = RGB( 92, 71, 0) // #$5C4700 | ||
+ | .const l_red = RGB(187, | ||
+ | .const d_grey | ||
+ | .const grey = RGB(128, | ||
+ | .const l_green = RGB(172, | ||
+ | .const l_blue | ||
+ | .const l_grey | ||
+ | |||
+ | .const Black | ||
+ | .const White | ||
+ | .const Red | ||
+ | .const Cyan = cyan.r * 65536 + cyan.g * 256 + cyan.b | ||
+ | .const Purple | ||
+ | .const Green | ||
+ | .const Blue = blue.r * 65536 + blue.g * 256 + blue.b | ||
+ | .const Yellow | ||
+ | .const L_brown = l_brown.r * 65536 + l_brown.g * 256 + l_brown.b | ||
+ | .const D_brown = d_brown.r * 65536 + d_brown.g * 256 + d_brown.b | ||
+ | .const L_red | ||
+ | .const D_grey | ||
+ | .const Grey = grey.r * 65536 + grey.g * 256 + grey.b | ||
+ | .const L_green = l_green.r * 65536 + l_green.g * 256 + l_green.b | ||
+ | .const L_blue | ||
+ | .const L_grey | ||
+ | </ | ||
+ | |||
+ | Note that You have to put in the correct values here as this only serves as example. Putting wrong RGB collors will make the code below fail! If You don't like this approach, there is a routine below by Pantaloon to determine the collors or You could add line 201 to the image and read out the values directly from the 0 to 15th pixel. In short, feel free to modify. | ||
+ | |||
+ | This following snippet converts a 320x200 .png file directly to koala format and you can specify where you want the data to end up. The routine will automatically detect the Background Color and set the bitmap patterns accordingly. Made by TWW/ | ||
+ | |||
+ | < | ||
+ | .macro PNGtoKOALA(PNGpicture, | ||
+ | |||
+ | // Create RGB to C64 color index | ||
+ | .var RGBtoC64 = Hashtable() | ||
+ | .eval RGBtoC64.put(Black, | ||
+ | .eval RGBtoC64.put(White, | ||
+ | .eval RGBtoC64.put(Red, | ||
+ | .eval RGBtoC64.put(Cyan, | ||
+ | .eval RGBtoC64.put(Purple, | ||
+ | .eval RGBtoC64.put(Green, | ||
+ | .eval RGBtoC64.put(Blue, | ||
+ | .eval RGBtoC64.put(Yellow, | ||
+ | .eval RGBtoC64.put(L_brown, | ||
+ | .eval RGBtoC64.put(D_brown, | ||
+ | .eval RGBtoC64.put(L_red, | ||
+ | .eval RGBtoC64.put(D_grey, | ||
+ | .eval RGBtoC64.put(Grey, | ||
+ | .eval RGBtoC64.put(L_green, | ||
+ | .eval RGBtoC64.put(L_blue, | ||
+ | .eval RGBtoC64.put(L_grey, | ||
+ | |||
+ | // Hashtable for Storing all the colors. | ||
+ | .var RGBColorTable | ||
+ | |||
+ | // Create a list to hold all the Bitmap and collor data | ||
+ | .var AllData = List(10000) | ||
+ | |||
+ | // Load the picture into the data list Graphics | ||
+ | .var Graphics = LoadPicture(PNGpicture, | ||
+ | |||
+ | // Convert and return the Background Color | ||
+ | .var BackgroundColor = FindBackgroundColor() | ||
+ | |||
+ | .pc = BMPData "KOALA - Bitmap Graphics" | ||
+ | .fill 8000, | ||
+ | .pc = Chardata "KOALA - Character Color Data" | ||
+ | .fill 1000, | ||
+ | .pc = D800data "KOALA - D800 Color Data" | ||
+ | .fill 1000, | ||
+ | .pc = BGC "KOALA - Background Collor" | ||
+ | .fill 1, | ||
+ | |||
+ | } | ||
+ | |||
+ | // | ||
+ | .function FindBackgroundColor() { | ||
+ | |||
+ | // Hastable for storing 4x8 block colordata | ||
+ | .var BlockColors = Hashtable() | ||
+ | |||
+ | // Hashtable for potential background Colors from inside one block | ||
+ | .var BG = Hashtable() | ||
+ | |||
+ | // List for keeping track of background color candidates from all blocks | ||
+ | .var BGCandidate | ||
+ | |||
+ | // Declare some variables | ||
+ | .var CurrentBlock = 0 // Keeps track of which block is being checked | ||
+ | .var BGRemaining | ||
+ | .var ColorCounter = 0 // Counter for keeping track of how many colors are found inside each block | ||
+ | .var FirstMatch | ||
+ | |||
+ | // Loop for checking all 1000 blocks | ||
+ | .for (CurrentBlock=0 ; CurrentBlock< | ||
+ | |||
+ | // Clear out any block colors from the hashtable | ||
+ | .eval BlockColors = Hashtable() | ||
+ | |||
+ | // Fetch 4x8 pixel block colors (32 total) | ||
+ | .for (var Pixel = 0 ; Pixel < 32 ; Pixel++) { | ||
+ | .var PixelColor = Graphics.getPixel([8*CurrentBlock+[[Pixel<< | ||
+ | .eval BlockColors.put(PixelColor, | ||
+ | } | ||
+ | |||
+ | // Reset the block color counter | ||
+ | .eval ColorCounter = 0 | ||
+ | |||
+ | // Store the block colors in BG | ||
+ | .if (BlockColors.containsKey(Black) | ||
+ | .if (BlockColors.containsKey(White) | ||
+ | .if (BlockColors.containsKey(Red) | ||
+ | .if (BlockColors.containsKey(Cyan) | ||
+ | .if (BlockColors.containsKey(Purple) ==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(Green) | ||
+ | .if (BlockColors.containsKey(Blue) | ||
+ | .if (BlockColors.containsKey(Yellow) ==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(L_brown)==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(D_brown)==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(L_red) | ||
+ | .if (BlockColors.containsKey(D_grey) ==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(Grey) | ||
+ | .if (BlockColors.containsKey(L_green)==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(L_blue) ==true) { .eval BG.put(ColorCounter, | ||
+ | .if (BlockColors.containsKey(L_grey) ==true) { .eval BG.put(ColorCounter, | ||
+ | |||
+ | // Carry out a background color check when there are 4 colors in a block | ||
+ | .if (ColorCounter == 4 && BGCandidate.size()> | ||
+ | |||
+ | // Check if it is the first block with 4 collors | ||
+ | .if (FirstMatch) { | ||
+ | |||
+ | // Copy the 4 collors as possible candidates | ||
+ | .eval BGCandidate.add(BG.get(0)) | ||
+ | .eval BGCandidate.add(BG.get(1)) | ||
+ | .eval BGCandidate.add(BG.get(2)) | ||
+ | .eval BGCandidate.add(BG.get(3)) | ||
+ | .eval FirstMatch = false | ||
+ | } else { | ||
+ | .for (var i = 0 ; i < BGCandidate.size() ; i++) { | ||
+ | .if (BGCandidate.get(i) != BG.get(0)) { | ||
+ | .if (BGCandidate.get(i) != BG.get(1)) { | ||
+ | .if (BGCandidate.get(i) != BG.get(2)) { | ||
+ | .if (BGCandidate.get(i) != BG.get(3)) { | ||
+ | .eval BGCandidate.remove(i) | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | .var BackgroundColor = BGCandidate.get(0) | ||
+ | |||
+ | // Variable for keeping track of which byte is in use | ||
+ | .var ByteNumber = 0 | ||
+ | |||
+ | // Create hashtable and ascociate bitmap patterns to RGB Colors (one for bit patterns and one for collor referance) | ||
+ | .var ColorIndex = Hashtable() | ||
+ | .var ColorIndex2 = Hashtable() | ||
+ | |||
+ | // Define the BG Color into the Color Indexes | ||
+ | .eval ColorIndex.put(BackgroundColor, | ||
+ | .eval ColorIndex2.put(0, | ||
+ | |||
+ | .for (var BlockNumber = 0 ; BlockNumber < 1000 ; BlockNumber++) { | ||
+ | |||
+ | // Variable for keeping track of which collor is to be used inside the block | ||
+ | .var colpos = 1 | ||
+ | |||
+ | // Place the RGB color data into the color indexes (Multicolor Bit-combinations 01, 10 & 11 assigned the 3 colors) | ||
+ | .for (var i = 0 ; i < 4 ; i++) { | ||
+ | .if (RGBColorTable.get(i+[BlockNumber*4]) != BackgroundColor) { | ||
+ | .if (RGBColorTable.get(i+[BlockNumber*4]) != null) { | ||
+ | .eval ColorIndex.put(RGBColorTable.get(i+[BlockNumber*4]), | ||
+ | .eval ColorIndex2.put(colpos, | ||
+ | .eval colpos = colpos+1 | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Read Pixel Collors in current block and fill in BMPData accordingly | ||
+ | .for (var Byte = 0 ; Byte < 8 ; Byte++) { | ||
+ | |||
+ | // Temp Storage for bitmap byte, bitmap pattern and the pixelcolor | ||
+ | .var BMPByte = 0 | ||
+ | .var BMPPattern = 0 | ||
+ | .var PixelColor = 0 | ||
+ | |||
+ | // Find the pixel collors and cross ref. with the bit patterns to create the BMP data | ||
+ | .for (var Pixel = 0 ; Pixel < 4 ; Pixel++) { | ||
+ | .eval PixelColor = Graphics.getPixel([[8*BlockNumber]+[[Pixel<< | ||
+ | .eval BMPPattern = ColorIndex.get(PixelColor) | ||
+ | .eval BMPByte = BMPByte|[BMPPattern << [6 - Pixel*2]] | ||
+ | } | ||
+ | |||
+ | // Set the done BMP data into final data storage | ||
+ | .eval AllData.set(ByteNumber, | ||
+ | .eval ByteNumber = ByteNumber+1 | ||
+ | } | ||
+ | |||
+ | // Create the color data | ||
+ | .var CharacterColor = 0 | ||
+ | .var D800Color = 0 | ||
+ | |||
+ | .if (RGBtoC64.get(ColorIndex2.get(1)) != null) { .eval CharacterColor = [RGBtoC64.get(ColorIndex2.get(1))<< | ||
+ | .if (RGBtoC64.get(ColorIndex2.get(2)) != null) { .eval CharacterColor = CharacterColor|RGBtoC64.get(ColorIndex2.get(2)) } | ||
+ | .if (RGBtoC64.get(ColorIndex2.get(3)) != null) { .eval D800Color = RGBtoC64.get(ColorIndex2.get(3)) } | ||
+ | |||
+ | // Store the colors into final data storage | ||
+ | .eval AllData.set(8000+BlockNumber, | ||
+ | .eval AllData.set(9000+BlockNumber, | ||
+ | } | ||
+ | |||
+ | // Return background color: | ||
+ | .return BackgroundColor | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Load SingleColor HiRes format straight from .png file ==== | ||
+ | |||
+ | Macro to load those HiRES pictures with more than 2 colors into Kickass. by TWW/CTR | ||
+ | |||
+ | Read header for info. Make sure you update the RGB colors so they match your palete. | ||
+ | |||
+ | Sometimes the graphics can be inverted from 8x8 pixel block to the next but the result (in the color table) works anyway. | ||
+ | |||
+ | |||
+ | < | ||
+ | / | ||
+ | |||
+ | PNGtoHIRES | ||
+ | ~~~~~~~~~~ | ||
+ | |||
+ | By: TWW/CTR | ||
+ | |||
+ | USAGE | ||
+ | ~~~~~ | ||
+ | |||
+ | : | ||
+ | |||
+ | @SIGNATURE | ||
+ | @AUTHOR | ||
+ | |||
+ | @PARAM | ||
+ | @PARAM | ||
+ | @PARAM | ||
+ | |||
+ | |||
+ | EXAMPLES | ||
+ | ~~~~~~~~ | ||
+ | |||
+ | : | ||
+ | |||
+ | |||
+ | NOTES | ||
+ | ~~~~~ | ||
+ | |||
+ | For now, only handles 320x200 | ||
+ | |||
+ | |||
+ | IMPROVEMENTS | ||
+ | ~~~~~~~~~~~~ | ||
+ | |||
+ | Add variable picture sizes | ||
+ | Handle assertions if the format is unsupported (size, color restrictions etc.) | ||
+ | |||
+ | TODO | ||
+ | ~~~~ | ||
+ | |||
+ | |||
+ | BUGS | ||
+ | ~~~~ | ||
+ | |||
+ | |||
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | ||
+ | |||
+ | .macro PNGtoHIRES(PNGpicture, | ||
+ | |||
+ | .var Graphics = LoadPicture(PNGpicture) | ||
+ | |||
+ | // Graphics RGB Colors. Must be adapted to the graphics | ||
+ | .const C64Black | ||
+ | .const C64White | ||
+ | .const C64Red | ||
+ | .const C64Cyan | ||
+ | .const C64Purple | ||
+ | .const C64Green | ||
+ | .const C64Blue | ||
+ | .const C64Yellow | ||
+ | .const C64L_brown = 111 * 65536 + 079 * 256 + 037 | ||
+ | .const C64D_brown = 067 * 65536 + 057 * 256 + 000 | ||
+ | .const C64L_red | ||
+ | .const C64D_grey | ||
+ | .const C64Grey | ||
+ | .const C64L_green = 1 * 65536 + 0 * 256 + 0 | ||
+ | .const C64L_blue | ||
+ | .const C64L_grey | ||
+ | |||
+ | // Add the colors neatly into a Hashtable for easy lookup reference | ||
+ | .var ColorTable = Hashtable() | ||
+ | .eval ColorTable.put(C64Black, | ||
+ | .eval ColorTable.put(C64White, | ||
+ | .eval ColorTable.put(C64Red, | ||
+ | .eval ColorTable.put(C64Cyan, | ||
+ | .eval ColorTable.put(C64Purple, | ||
+ | .eval ColorTable.put(C64Green, | ||
+ | .eval ColorTable.put(C64Blue, | ||
+ | .eval ColorTable.put(C64Yellow, | ||
+ | .eval ColorTable.put(C64L_brown, | ||
+ | .eval ColorTable.put(C64D_brown, | ||
+ | .eval ColorTable.put(C64L_red, | ||
+ | .eval ColorTable.put(C64D_grey, | ||
+ | .eval ColorTable.put(C64Grey, | ||
+ | .eval ColorTable.put(C64L_green, | ||
+ | .eval ColorTable.put(C64L_blue, | ||
+ | .eval ColorTable.put(C64L_grey, | ||
+ | |||
+ | .pc = BMPData "Hires Bitmap" | ||
+ | |||
+ | .var ScreenMem = List() | ||
+ | .for (var Line = 0 ; Line < 200 ; Line = Line + 8) { | ||
+ | .for (var Block = 0 ; Block < 320 ; Block=Block+8) { | ||
+ | .var Coll1 = Graphics.getPixel(Block, | ||
+ | .var Coll2 = 0 | ||
+ | .for (var j = 0 ; j < 8 ; j++ ) { | ||
+ | .var ByteValue = 0 | ||
+ | .for (var i = 0 ; i < 8 ; i++ ) { | ||
+ | .if (Graphics.getPixel(Block, | ||
+ | .if (Graphics.getPixel(Block, | ||
+ | } | ||
+ | .byte ByteValue | ||
+ | } | ||
+ | .var BlockColor = [ColorTable.get(Coll2)]*16+ColorTable.get(Coll1) | ||
+ | .eval ScreenMem.add(BlockColor) | ||
+ | } | ||
+ | } | ||
+ | .pc = ColData "Hires Color Data" | ||
+ | ScreenMemColors: | ||
+ | .for (var i = 0 ; i < 1000 ; i++ ) { | ||
+ | .byte ScreenMem.get(i) | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Load sprite data straight from .gif file ==== | ||
+ | |||
+ | First example assumes that each sprite is saved in a separate .gif file: | ||
+ | |||
+ | < | ||
+ | //Example by Slammer | ||
+ | .pc = $3000 " | ||
+ | spriteData: | ||
+ | |||
+ | : | ||
+ | : | ||
+ | : | ||
+ | // etc | ||
+ | |||
+ | .macro LoadSpriteFromPicture(filename) { | ||
+ | .var picture = LoadPicture(filename, | ||
+ | .for (var y=0; y<21; y++) | ||
+ | .for (var x=0; x<3; x++) | ||
+ | .byte picture.getMulticolorByte(x, | ||
+ | .byte 0 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Second example is an adaption of the above script, which loads a single picture with 8 sprites organized horizontally: | ||
+ | |||
+ | < | ||
+ | // | ||
+ | .var spriteData = $3000 | ||
+ | .pc = spriteData " | ||
+ | .var spritePic = LoadPicture(" | ||
+ | .for (var i=0; i<8; i++) | ||
+ | : | ||
+ | |||
+ | .macro getSprite(spritePic, | ||
+ | .for (var y=0; y<21; y++) | ||
+ | .for (var x=0; x<3; x++) | ||
+ | .byte spritePic.getMulticolorByte(x + spriteNo * 3, y) | ||
+ | .byte 0 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The values in the second argument of the LoadPicture function is the rgb values of the colors asigned to the four bit combinations (00, 01, 10 and 11). To find which colors are used in your picture you can use a paint program. Another posibility is to use the script below which will print the colors used in the picture ' | ||
+ | |||
+ | < | ||
+ | .var logo = LoadPicture(" | ||
+ | |||
+ | .var colors = Hashtable() | ||
+ | .for (var x=0; x< | ||
+ | .for (var y=0; y< | ||
+ | .eval colors.put(logo.getPixel(x, | ||
+ | |||
+ | .var colorSet = colors.keys() | ||
+ | .for (var i=0; i< | ||
+ | .print " | ||
+ | </ | ||
+ | ==== Convert picture into a charset ==== | ||
+ | |||
+ | This is a so called "equal char packer" | ||
+ | |||
+ | < | ||
+ | .macro equalCharPack(filename, | ||
+ | .var charMap = Hashtable() | ||
+ | .var charNo = 0 | ||
+ | .var screenData = List() | ||
+ | .var charsetData = List() | ||
+ | .var pic = LoadPicture(filename) | ||
+ | |||
+ | // Graphics should fit in 8x8 Single collor / 4 x 8 Multi collor blocks | ||
+ | .var PictureSizeX = pic.width/8 | ||
+ | .var PictureSizeY = pic.height/ | ||
+ | |||
+ | .for (var charY=0; charY< | ||
+ | .for (var charX=0; charX< | ||
+ | .var currentCharBytes = List() | ||
+ | .var key = "" | ||
+ | .for (var i=0; i<8; i++) { | ||
+ | .var byteVal = pic.getSinglecolorByte(charX, | ||
+ | .eval key = key + toHexString(byteVal) + "," | ||
+ | .eval currentCharBytes.add(byteVal) | ||
+ | } | ||
+ | .var currentChar = charMap.get(key) | ||
+ | .if (currentChar == null) { | ||
+ | .eval currentChar = charNo | ||
+ | .eval charMap.put(key, | ||
+ | .eval charNo++ | ||
+ | .for (var i=0; i<8; i++) { | ||
+ | .eval charsetData.add(currentCharBytes.get(i)) | ||
+ | } | ||
+ | } | ||
+ | .eval screenData.add(currentChar) | ||
+ | } | ||
+ | } | ||
+ | .pc = screenAdr " | ||
+ | .fill screenData.size(), | ||
+ | .pc = charsetAdr " | ||
+ | .fill charsetData.size(), | ||
+ | } | ||
+ | </ | ||
+ | It is used simply like this: | ||
+ | < | ||
+ | : | ||
+ | </ | ||
+ | |||
+ | ==== Match RGB colors with the C64 palette ==== | ||
+ | |||
+ | By Pantaloon/ | ||
+ | |||
+ | Here is some code to find the closest c-64 color index from an RGB value, useful when doing image conversion. There are of course better ways to find the best match but this works ok if you have images with c-64 colors already. I use it for my kickasm image converters. | ||
+ | |||
+ | < | ||
+ | .struct RGB {r,g,b} | ||
+ | |||
+ | .var s_palette = List().add( | ||
+ | RGB(0, | ||
+ | RGB(255, | ||
+ | RGB(104, | ||
+ | RGB(131, | ||
+ | RGB(111, | ||
+ | RGB(89, | ||
+ | RGB(65, | ||
+ | RGB(184, | ||
+ | RGB(209, | ||
+ | RGB(67, | ||
+ | RGB(154, | ||
+ | RGB(91, | ||
+ | RGB(142, | ||
+ | RGB(157, | ||
+ | RGB(117, | ||
+ | RGB(193, | ||
+ | ); | ||
+ | |||
+ | .function colorDistance(c1, | ||
+ | { | ||
+ | .var cr = c1.r-c2.r | ||
+ | .var cg = c1.g-c2.g | ||
+ | .var cb = c1.b-c2.b | ||
+ | .return sqrt([cr*cr] + [cg*cg] + [cb*cb]) | ||
+ | } | ||
+ | |||
+ | .function getClosestColorIndex(rgb) | ||
+ | { | ||
+ | .return getClosestColorIndex( | ||
+ | rgb, s_palette | ||
+ | ) | ||
+ | } | ||
+ | |||
+ | .function getClosestColorIndex(rgb, | ||
+ | { | ||
+ | .var distance = colorDistance(rgb, | ||
+ | .var closestColorIndex = 0 | ||
+ | |||
+ | .for (var index = 1; index < palette.size(); | ||
+ | { | ||
+ | .var d = colorDistance(rgb, | ||
+ | .if (d < distance) | ||
+ | { | ||
+ | .eval distance = d | ||
+ | .eval closestColorIndex = index | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .return closestColorIndex | ||
+ | } | ||
+ | </ | ||
+ | ===== Pseudo Commands ===== | ||
+ | Pseudo commands are macros that take assembler arguments. This means they know the difference between #00, $10 and ($10),y. This section will show some examples of how to create pseudo commands. | ||
+ | |||
+ | |||
+ | |||
+ | ==== Repetition Commands ==== | ||
+ | Every body who have programmed c64 assembler language have tried to repeat the same command several time. If you want to divide by 8 you type 3 lsr commands or if you want to create a pause you can do alot of nop's. To save alot of typing, you can create repetitive pseudo commands. | ||
+ | |||
+ | < | ||
+ | :lsr #3 // divide by 8 | ||
+ | :nop #8 // Do 8 nops | ||
+ | </ | ||
+ | The commands (+ some extra) are defined like this: | ||
+ | |||
+ | < | ||
+ | // | ||
+ | // repetition commands | ||
+ | // | ||
+ | .macro ensureImmediateArgument(arg) { | ||
+ | .if (arg.getType()!=AT_IMMEDIATE) .error "The argument must be immediate!" | ||
+ | } | ||
+ | .pseudocommand asl x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | .pseudocommand lsr x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | .pseudocommand rol x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | .pseudocommand ror x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | |||
+ | .pseudocommand pla x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | |||
+ | .pseudocommand nop x { | ||
+ | : | ||
+ | .for (var i=0; i< | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ==== Creating a Pause Command ==== | ||
+ | Often you want to time your code precisely. To make things a little easier you can create a pause command that pause a given amount of cycles. Eg: | ||
+ | |||
+ | < | ||
+ | :pause #10 // Waits 10 cycles | ||
+ | :pause2 #63 // Waits 63 cycles | ||
+ | </ | ||
+ | |||
+ | Here you will see two versions of the pause command. The first one only uses bit and nops. Both are based on some of the pseudo commands defined in earlier sections: | ||
+ | < | ||
+ | .pseudocommand pause cycles { | ||
+ | : | ||
+ | .var x = floor(cycles.getValue()) | ||
+ | .if (x<2) .error "Cant make a pause on " + x + " cycles" | ||
+ | |||
+ | // Take care of odd cyclecount | ||
+ | .if ([x& | ||
+ | bit $00 | ||
+ | .eval x=x-3 | ||
+ | } | ||
+ | |||
+ | // Take care of the rest | ||
+ | .if (x>0) | ||
+ | :nop #x/2 | ||
+ | } | ||
+ | </ | ||
+ | The second one uses a loop. This makes it take less memory, however the timing will not be correct if the conditional jump crosses a page boundary: | ||
+ | < | ||
+ | .pseudocommand pause2 cycles { | ||
+ | : | ||
+ | .var x = floor(cycles.getValue()) | ||
+ | .if (x<2) .error "Cant make a pause on " + x + " cycles" | ||
+ | |||
+ | // Make a delay loop | ||
+ | .if (x>=11) { | ||
+ | .const cfirst = 6 // cycles for first loop | ||
+ | .const cextra = 5 // cycles for extra loops | ||
+ | .var noOfLoops = 1+floor([x-cfirst]/ | ||
+ | .eval x = x - cfirst - [noOfLoops-1]*cextra | ||
+ | .if (x==1){ | ||
+ | .eval x=x+cextra | ||
+ | .eval noOfLoops-- | ||
+ | } | ||
+ | ldy #noOfLoops | ||
+ | dey | ||
+ | bne *-1 | ||
+ | } | ||
+ | |||
+ | // Take care of odd cyclecount | ||
+ | .if ([x& | ||
+ | bit $00 | ||
+ | .eval x=x-3 | ||
+ | } | ||
+ | |||
+ | // Take care of the rest | ||
+ | .if (x>0) | ||
+ | :nop #x/2 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====Pseudocommands for 16 bit operations==== | ||
+ | |||
+ | The following pseudocommands uses the function _16bit_nextArgument(arg), | ||
+ | < | ||
+ | .function _16bit_nextArgument(arg) { | ||
+ | .if (arg.getType()==AT_IMMEDIATE) | ||
+ | .return CmdArgument(arg.getType(),> | ||
+ | .return CmdArgument(arg.getType(), | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Increment a 16bit variable: | ||
+ | < | ||
+ | .pseudocommand inc16 arg { | ||
+ | inc arg | ||
+ | bne over | ||
+ | inc _16bit_nextArgument(arg) | ||
+ | over: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Decrement a 16 bit variable: | ||
+ | < | ||
+ | .pseudocommand dec16 arg { | ||
+ | lda arg | ||
+ | | ||
+ | dec _16bit_nextArgument(arg) | ||
+ | skip: dec arg | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Move or Load 16 bit values from/to 16 bit variables: | ||
+ | < | ||
+ | .pseudocommand mov16 src;tar { | ||
+ | lda src | ||
+ | sta tar | ||
+ | lda _16bit_nextArgument(src) | ||
+ | sta _16bit_nextArgument(tar) | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Addition of 16 bits variables: | ||
+ | < | ||
+ | .pseudocommand add16 arg1 ; arg2 ; tar { | ||
+ | .if (tar.getType()==AT_NONE) .eval tar=arg1 | ||
+ | lda arg1 | ||
+ | adc arg2 | ||
+ | sta tar | ||
+ | lda _16bit_nextArgument(arg1) | ||
+ | adc _16bit_nextArgument(arg2) | ||
+ | sta _16bit_nextArgument(tar) | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Equal char packer ===== | ||
+ | |||
+ | Cruzer made an equal char packer using a hash table. It packs a 320x200 hires pic, but it should be easy to change it to multicolor and extend the size... | ||
+ | |||
+ | < | ||
+ | .macro equalCharPack(filename, | ||
+ | .var charMap = Hashtable() | ||
+ | .var charNo = 0 | ||
+ | .var screenData = List() | ||
+ | .var charsetData = List() | ||
+ | .var pic = LoadPicture(filename) | ||
+ | .for (var charY=0; charY< | ||
+ | .for (var charX=0; charX< | ||
+ | .var currentCharBytes = List() | ||
+ | .var key = "" | ||
+ | .for (var i=0; i<8; i++) { | ||
+ | .var byteVal = pic.getSinglecolorByte(charX, | ||
+ | .eval key = key + toHexString(byteVal) + "," | ||
+ | .eval currentCharBytes.add(byteVal) | ||
+ | } | ||
+ | .var currentChar = charMap.get(key) | ||
+ | .if (currentChar == null) { | ||
+ | .eval currentChar = charNo | ||
+ | .eval charMap.put(key, | ||
+ | .eval charNo++ | ||
+ | .for (var i=0; i<8; i++) { | ||
+ | .eval charsetData.add(currentCharBytes.get(i)) | ||
+ | } | ||
+ | } | ||
+ | .eval screenData.add(currentChar) | ||
+ | } | ||
+ | } | ||
+ | .pc = screenAdr " | ||
+ | .fill screenData.size(), | ||
+ | .pc = charsetAdr " | ||
+ | .fill charsetData.size(), | ||
+ | } | ||
+ | |||
+ | : | ||
+ | </ | ||
+ | |||
+ | ===== Set Screen and Char Location ===== | ||
+ | < | ||
+ | .macro SetScreenAndCharLocation(screen, | ||
+ | lda # | ||
+ | sta $D018 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Clear Screen ===== | ||
+ | < | ||
+ | .macro ClearScreen(screen, | ||
+ | lda #clearByte | ||
+ | ldx #0 | ||
+ | !loop: | ||
+ | sta screen, x | ||
+ | sta screen + $100, x | ||
+ | sta screen + $200, x | ||
+ | sta screen + $300, x | ||
+ | inx | ||
+ | bne !loop- | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Clear Color RAM ===== | ||
+ | < | ||
+ | .macro ClearColorRam(clearByte) { | ||
+ | lda #clearByte | ||
+ | ldx #0 | ||
+ | !loop: | ||
+ | sta $D800, x | ||
+ | sta $D800 + $100, x | ||
+ | sta $D800 + $200, x | ||
+ | sta $D800 + $300, x | ||
+ | inx | ||
+ | bne !loop- | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Set Various VIC Banks ===== | ||
+ | < | ||
+ | // $DD00 = %xxxxxx11 -> bank0: $0000-$3fff | ||
+ | // $DD00 = %xxxxxx10 -> bank1: $4000-$7fff | ||
+ | // $DD00 = %xxxxxx01 -> bank2: $8000-$bfff | ||
+ | // $DD00 = %xxxxxx00 -> bank3: $c000-$ffff | ||
+ | .macro SetVICBank0() { | ||
+ | lda $DD00 | ||
+ | and #%11111100 | ||
+ | ora #%00000011 | ||
+ | sta $DD00 | ||
+ | } | ||
+ | |||
+ | .macro SetVICBank1() { | ||
+ | lda $DD00 | ||
+ | and #%11111100 | ||
+ | ora #%00000010 | ||
+ | sta $DD00 | ||
+ | } | ||
+ | |||
+ | .macro SetVICBank2() { | ||
+ | lda $DD00 | ||
+ | and #%11111100 | ||
+ | ora #%00000001 | ||
+ | sta $DD00 | ||
+ | } | ||
+ | |||
+ | .macro SetVICBank3() { | ||
+ | lda $DD00 | ||
+ | and #%11111100 | ||
+ | ora #%00000000 | ||
+ | sta $DD00 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Set Color Modes, Colors and Scroll ===== | ||
+ | < | ||
+ | .macro SetBorderColor(color) { | ||
+ | lda #color | ||
+ | sta $d020 | ||
+ | } | ||
+ | |||
+ | .macro SetBackgroundColor(color) { | ||
+ | lda #color | ||
+ | sta $d021 | ||
+ | } | ||
+ | |||
+ | .macro SetMultiColor1(color) { | ||
+ | lda #color | ||
+ | sta $d022 | ||
+ | } | ||
+ | |||
+ | .macro SetMultiColor2(color) { | ||
+ | lda #color | ||
+ | sta $d023 | ||
+ | } | ||
+ | |||
+ | .macro SetMultiColorMode() { | ||
+ | lda $d016 | ||
+ | ora #16 | ||
+ | sta $d016 | ||
+ | } | ||
+ | |||
+ | .macro SetScrollMode() { | ||
+ | lda $D016 | ||
+ | eor #%00001000 | ||
+ | sta $D016 | ||
+ | } | ||
+ | </ |
base/kick_assembler_macros.txt · Last modified: 2016-08-10 02:24 by tww_ctr