Shaun_B

Commodore C64/SuperCPU programming API in C

Sep 19th, 2021 (edited)
329
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #pragma target(cx16)
  2.  
  3. unsigned byte getMachineType;
  4.  
  5. /**
  6.  * C64 API intended for use with the SuperCPU
  7.  * By Donkeysoft MMXXI
  8.  * At the moment, KickC and Kick Assembler only
  9.  * build 65c02 binaries; this may change in the
  10.  * future.
  11.  */
  12. void main() {
  13.     setMachineType();
  14.     disableBasicInterrupts();
  15.     poke(0xd020, 0);
  16.     poke(0xd021, 15);
  17.  
  18.     clearScreen();
  19.  
  20.     printAt(4, 12, "c64 rulez!");
  21.     colourAt(4, 12, 1, 10);
  22.  
  23.     unsigned short xLocation = 64;
  24.     unsigned char yLocation = 48;
  25.  
  26.     for(unsigned char index = 0; index < 8; index++) {
  27.         setSpriteColour(index, index);
  28.         switchOnSprites(index);
  29.         spriteAt(xLocation, yLocation, index);
  30.         xLocation += 24;
  31.         yLocation += 24;
  32.     }
  33.  
  34.     spriteExpandX(0);
  35.     spriteExpandY(1);
  36.     spriteUnexpandX(2);
  37.     spriteUnexpandY(3);
  38.  
  39.     unsigned short waitState = 256;
  40.  
  41.     while(1) {
  42.         if(checkForScanLine(44) == 1){
  43.             poke(53280,1);
  44.             wait(waitState);
  45.             poke(53280,0);
  46.         }
  47.         if(checkForScanLine(88) == 1){
  48.             poke(53280,3);
  49.             wait(waitState);
  50.             poke(53280,0);
  51.         }
  52.         if(checkForScanLine(244) == 1){
  53.             poke(53280,5);
  54.             wait(waitState);
  55.             poke(53280,0);
  56.         }
  57.         if(checkForScanLine(272) == 1){
  58.             poke(53280,7);
  59.             wait(waitState);
  60.             poke(53280,0);
  61.         }
  62.     }
  63. }
  64.  
  65. /**
  66.  * This is the initialisation will
  67.  * determine the machine type
  68.  * by setting the getMachineType global
  69.  * as follows:
  70.  *  37 is PAL
  71.  *  5 is NTSC (old)
  72.  *  6 is NTSC (new)
  73.  *  0 (or any other value) is unknown
  74.  *
  75.  * For safety, the initial value of 0xc000
  76.  * is stored into the accumulator and
  77.  * pushed onto the stack; it is then
  78.  * restored after the getMachineType
  79.  * global is set
  80.  *
  81.  * @author Robin Harbron
  82.  */
  83. void setMachineType() {
  84.     asm {
  85.         lda $c000
  86.         pha
  87.         sei
  88.     __br1:
  89.         lda $d011
  90.         bmi __br1
  91.     __br2:
  92.         lda $d011
  93.         bpl __br2
  94.     __br3:
  95.         lda $d012
  96.         bit $d011
  97.         bpl __ex1
  98.         sta $c000
  99.         bmi __br3
  100.     __ex1:
  101.         cli
  102.     }
  103.  
  104.     getMachineType = peek(0xc000);
  105.  
  106.     asm {
  107.         pla
  108.         sta $c000
  109.     }
  110. }
  111.  
  112. /**
  113.  * Takes in two 16-bit values, the first is
  114.  * the memory location and the second will be
  115.  * converted to an 8-bit value which is then
  116.  * poked to memory
  117.  */
  118. void poke(unsigned short location, unsigned short value) {
  119.     char* loc = location;
  120.     unsigned byte val = value & 255;
  121.  
  122.     *(loc) = val;
  123. }
  124.  
  125. /**
  126.  * Returns a single byte from a specific
  127.  * memory location
  128.  */
  129. unsigned char peek(unsigned short location) {
  130.     unsigned char* memory = (char*)location;
  131.     unsigned char returnByte = *memory;
  132.  
  133.     return returnByte;
  134. }
  135.  
  136. /**
  137.  * Disables BASIC interrupts
  138.  */
  139. void disableBasicInterrupts() {
  140.     unsigned byte basicInterruptStatus = peek(0xdc0e);
  141.     poke(0xdc0e, basicInterruptStatus & 0xfe);
  142. }
  143.  
  144. /**
  145.  * Writes a string to the screen starting
  146.  * from column x and row y (zero indexed)
  147.  */
  148. void printAt(unsigned byte x, unsigned byte y, unsigned char* toPrint) {
  149.     unsigned short screenAddress = 0x0400 + x + (y * 40);
  150.     unsigned char index = 0;
  151.  
  152.     while(*(toPrint + index)) {
  153.         poke(screenAddress++, *(toPrint + index));
  154.         index++;
  155.     }
  156. }
  157.  
  158. /**
  159.  * Changes the character colour of a
  160.  * screen location starting from column
  161.  * x and row y (zero indexed)
  162.  */
  163. void colourAt(unsigned byte x, unsigned byte y, unsigned byte colour, unsigned byte numberOfCharacters) {
  164.     unsigned short colourRamAddress = 0xd800 + x + (y * 40);
  165.  
  166.     for(unsigned char index = 0; index < numberOfCharacters; index++) {
  167.         poke(colourRamAddress++, colour);
  168.     }
  169. }
  170.  
  171. /**
  172.  * Will switch on a single sprite of the
  173.  * eight available (zero indexed)
  174.  */
  175. void switchOnSprites(unsigned char spriteNumber) {
  176.     if(spriteNumber < 8) {
  177.         unsigned byte toEnable = peek(0xd015) | getBitNumber(spriteNumber);
  178.  
  179.         poke(0xd015, toEnable);
  180.     }
  181. }
  182.  
  183. /**
  184.  * Simple wait function - will count down
  185.  * to zero; accepted parameters is 1 - 65535
  186.  */
  187. void wait(unsigned short toWaitFor) {
  188.     if(!toWaitFor){
  189.         return;
  190.     }
  191.  
  192.     for(; toWaitFor > 0; --toWaitFor) {
  193.         ;;
  194.     }
  195. }
  196.  
  197. /**
  198.  * Will switch off a single sprite of the
  199.  * eight available (zero indexed)
  200.  */
  201. void switchOffSprites(unsigned char spriteNumber) {
  202.     if(spriteNumber < 8) {
  203.         unsigned byte toDisable = peek(0xd015) ^ getBitNumber(spriteNumber);
  204.  
  205.         poke(0xd015, toDisable);
  206.     }
  207. }
  208.  
  209. /**
  210.  * Set sprite to double width (zero indexed)
  211.  */
  212. void spriteExpandX(unsigned byte spriteNumber) {
  213.     if(spriteNumber < 8) {
  214.         unsigned char toExpand = peek(0xd01d) | getBitNumber(spriteNumber);
  215.         poke(0xd01d, toExpand);
  216.     }
  217. }
  218.  
  219. /**
  220.  * Set sprite to double height (zero indexed)
  221.  */
  222. void spriteExpandY(unsigned byte spriteNumber) {
  223.     if(spriteNumber < 8) {
  224.         unsigned char toExpand = peek(0xd017) | getBitNumber(spriteNumber);
  225.         poke(0xd017, toExpand);
  226.     }
  227. }
  228.  
  229. /**
  230.  * Unset sprite double width (zero indexed)
  231.  */
  232. void spriteUnexpandX(unsigned byte spriteNumber) {
  233.     if(spriteNumber < 8) {
  234.         unsigned char toUnexpand = peek(0xd01d) ^ getBitNumber(spriteNumber);
  235.         poke(0xd01d, toUnexpand);
  236.     }
  237. }
  238.  
  239. /**
  240.  * Unset sprite double height (zero indexed)
  241.  */
  242. void spriteUnexpandY(unsigned byte spriteNumber) {
  243.     if(spriteNumber < 8) {
  244.         unsigned char toUnexpand = peek(0xd017) ^ getBitNumber(spriteNumber);
  245.         poke(0xd017, toUnexpand);
  246.     }
  247. }
  248.  
  249. /**
  250.  * Returns the value of the joystick; zero
  251.  * returns value of port 2, and 1 returns
  252.  * value of port 1; any number above this
  253.  * will return 0
  254.  */
  255. unsigned byte readJoystickPort(unsigned char portNumber) {
  256.     if(portNumber > 1) {
  257.         return 0;
  258.     }
  259.  
  260.     return peek(0xdc00 + portNumber);
  261. }
  262.  
  263. /**
  264.  * Positions a sprite at x pixels across
  265.  * and y pixels down according to the
  266.  * zero-indexed sprite number; note that
  267.  * x can exceed 255
  268.  */
  269. void spriteAt(unsigned short x, unsigned char y, unsigned byte spriteNumber) {
  270.     unsigned byte spriteBit = getBitNumber(spriteNumber);
  271.     unsigned byte sprite = 0;
  272.     unsigned byte highBit = peek(0xd010);
  273.     unsigned short spriteX = 0xd000;
  274.     unsigned short spriteY = 0xd001;
  275.  
  276.     if(x > 255 && (highBit & spriteBit) == 0) {
  277.         unsigned byte setBit = highBit | spriteBit;
  278.         poke(0xd010, setBit);
  279.     } else if(highBit > 0 && (highBit & spriteBit) == 1) {
  280.         unsigned byte unsetBit = highBit - spriteBit;
  281.         poke(0xd010, unsetBit);
  282.     }
  283.  
  284.     if(spriteNumber > 0) {
  285.         for(;sprite < spriteNumber; sprite++) {
  286.             spriteX += 2;
  287.             spriteY += 2;
  288.         }
  289.     }
  290.  
  291.     poke(spriteX, x);
  292.     poke(spriteY, y);
  293. }
  294.  
  295. /**
  296.  * Sets the main sprite colour by
  297.  * sprite numbe (zero indexed) and
  298.  * colour (0 - 16) - will return
  299.  * without altering if the spriteNumber
  300.  * is 8 or over.
  301.  */
  302. void setSpriteColour(unsigned char spriteNumber, unsigned char colour) {
  303.     if(spriteNumber < 8) {
  304.         poke(0xd027 + spriteNumber, colour);
  305.     }
  306. }
  307.  
  308. /**
  309.  * Returns a value from 0 - 7 as its binary
  310.  * equivalent, for instance, sending 3 to
  311.  * this function will return 4; sending 7
  312.  * will return 128
  313.  */
  314. unsigned char getBitNumber(unsigned char bit) {
  315.     unsigned char returnBit = 1;
  316.  
  317.     return returnBit << bit;
  318. }
  319.  
  320. /**
  321.  * From a 16-bit start position, it will store
  322.  * the byte value in toFill by a set number
  323.  * of bytes
  324.  */
  325. void fillMemory(unsigned short startPosition, unsigned byte toFill, unsigned short numberOfBytes) {
  326.     for(unsigned short index = 0; index < numberOfBytes; index++) {
  327.         poke(startPosition + index, toFill);
  328.     }
  329. }
  330.  
  331. /**
  332.  * Clears the screen according to
  333.  * the current screen memory location
  334.  * pointer at 0xd018
  335.  */
  336. void clearScreen() {
  337.     unsigned short screenLocation = getScreenMemoryLocation();
  338.     fillMemory(screenLocation, 32, 1000);
  339. }
  340.  
  341. /**
  342.  * Will switch VIC II bank by setting
  343.  * bit zero and one of 0xdd00. Any value
  344.  * above three will default to bank zero
  345.  */
  346. void switchVicBank(unsigned byte bank) {
  347.     unsigned byte vicBank = peek(0xdd00);
  348.     switch(bank) {
  349.         case 0:
  350.             poke(0xdd00, (vicBank & 0b11111100) | 0x03);
  351.             break;
  352.         case 1:
  353.             poke(0xdd00, (vicBank & 0b11111100) | 0x02);
  354.             break;
  355.         case 2:
  356.             poke(0xdd00, (vicBank & 0b11111100) | 0x01);
  357.             break;
  358.         case 3:
  359.             poke(0xdd00, vicBank & 0b11111100);
  360.             break;
  361.     }
  362. }
  363.  
  364. /**
  365.  * The location 0xd018 controls the screen
  366.  * and character memory as well as the
  367.  * bitmap mode; this function points at the
  368.  * character memory with a valid input of
  369.  * zero to seven inclusive
  370.  */
  371. void switchCharacterPointer(unsigned byte characterPointer) {
  372.     if(characterPointer < 8) {
  373.         characterPointer = characterPointer << 1;
  374.         unsigned byte characterMemory = peek(0xd018);
  375.         poke(0xd018, (characterMemory & 0b11110001) | characterPointer);
  376.     }
  377. }
  378.  
  379. /**
  380.  * This will switch the default screen
  381.  * memory; default is one, which points
  382.  * to 0x0400. Values are zero through to
  383.  * 16 inclusive
  384.  */
  385. void switchScreenMemory(unsigned byte screenPointer) {
  386.     if(screenPointer < 16) {
  387.         screenPointer = screenPointer << 4;
  388.         unsigned byte screenMemory = peek(0xd018);
  389.         poke(0xd018, (screenMemory & 0b00001111) | screenPointer);
  390.     }
  391. }
  392.  
  393. /**
  394.  * Returns the current screen location
  395.  * based on the high bits of 0xd018
  396.  */
  397. unsigned short getScreenMemoryLocation() {
  398.     unsigned byte screenPointer = peek(0xd018) >> 4;
  399.     unsigned short memoryLocation = 0;
  400.  
  401.     for(unsigned byte index = 0; index < screenPointer; index++) {
  402.         memoryLocation += 0x0400;
  403.     }
  404.     return memoryLocation;
  405. }
  406.  
  407. /**
  408.  * Returns the keyboard stroke as a
  409.  * byte
  410.  *
  411.  * @todo 2021-10-03
  412.  */
  413. unsigned char getKeyboardInput() {
  414.     return 0;
  415. }
  416.  
  417. /**
  418.  * Sets the C64's extended colour mode
  419.  * accepting the four colours available
  420.  * to set being between 0 - 15 inclusive;
  421.  * any value above 15 means "no change"
  422.  * This clears the bitmap and multi-colour
  423.  * mode before implementing the extended
  424.  * colour mode.
  425.  */
  426. void setExtendedColourMode(unsigned byte colour1, unsigned byte colour2, unsigned byte colour3, unsigned byte colour4) {
  427.     unsigned byte extendedColourMode = peek(0xd011);
  428.  
  429.     if((extendedColourMode & 0b01000000) == 0) {
  430.         unsigned byte multiColourMode = peek(0xd016);
  431.         poke(0xd016, (multiColourMode & 0b11101111));
  432.         poke(0xd011, (extendedColourMode & 0b10011111) | 64);
  433.     }
  434.  
  435.     if(colour1 > 15 && colour2 > 15 && colour3 > 15 && colour4 > 15) {
  436.         return;
  437.     }
  438.  
  439.     if(colour1 < 16){
  440.         poke(0xd021, colour1);
  441.     }
  442.  
  443.     if(colour2 < 16){
  444.         poke(0xd022, colour2);
  445.     }
  446.  
  447.     if(colour3 < 16){
  448.         poke(0xd023, colour3);
  449.     }
  450.  
  451.     if(colour4 < 16){
  452.         poke(0xd024, colour4);
  453.     }
  454. }
  455.  
  456. /**
  457.  * Switches off extended colour mode
  458.  */
  459. void unsetExtendedColourMode() {
  460.     unsigned byte extendedColourMode = peek(0xd011);
  461.     poke(0xd011, extendedColourMode & 0b10111111);
  462. }
  463.  
  464. /**
  465.  * Switches on Multi Colour Mode
  466.  * and ensures that the Extended Colour
  467.  * Mode flag is unset; calling this
  468.  * function with 1 or more will enable
  469.  * bitmap mode as well
  470.  */
  471. void setMultiColourMode(byte enableBitmap) {
  472.     if(enableBitmap) {
  473.         setBitmapMode();
  474.     }
  475.  
  476.     unsigned byte controlRegister = peek(0xd016);
  477.     poke(0xd016, (controlRegister & 0xff | 0b00010000));
  478. }
  479.  
  480. /**
  481.  * Sets bitmap mode on the 0xd011
  482.  * control register (unsets extended
  483.  * colour mode)
  484.  */
  485. void setBitmapMode() {
  486.     unsigned byte controlRegister = peek(0xd011);
  487.  
  488.     poke(0xd011, (controlRegister & 0b10111111) | 0b00100000);
  489. }
  490.  
  491. /**
  492.  * Will check for the current scan line
  493.  * position; returns -1 if the parameter
  494.  * sent is out of bounds (set to 312 assuming
  495.  * PAL - valid range is therefore 0 - 311
  496.  * assuming a zero index); 0 is returned if
  497.  * not at that scan line, and 1 if the
  498.  * parameter matches the current scan line
  499.  *
  500.  * @see setMachineType()
  501.  */
  502. signed char checkForScanLine(unsigned int scanLine) {
  503.     if(scanLine > 311 && getMachineType == 0x37) {
  504.         return -1;
  505.     }
  506.     if(scanLine > 261 && getMachineType == 0x06) {
  507.         return -1;
  508.     }
  509.     if(scanLine > 260 && getMachineType == 0x05) {
  510.         return -1;
  511.     }
  512.  
  513.     unsigned int currentScanLine = peek(0xd012);
  514.     unsigned byte highBit = peek(0xd011);
  515.     if(scanLine < 256) {
  516.         if(scanLine == currentScanLine && highBit < 128) {
  517.             return 1;
  518.         }
  519.     }
  520.  
  521.     if(highBit > 127 && scanLine > 255 ) {
  522.         currentScanLine += 256;
  523.         if(scanLine == currentScanLine) {
  524.             return 1;
  525.         }
  526.     }
  527.  
  528.     return 0;
  529. }
  530.  
RAW Paste Data