Skip to content

Programming by Design

If you're not prepared to be wrong, you'll never come up with anything original. – Sir Ken Robinson

  • About
  • Java-PbD
  • C-PbD
  • ASM-PbD
  • Algorithms
  • Other

Group Project 1 (of 2)

Posted on July 11, 2023February 3, 2025 By William Jojo
CISS-111-Project
CISS-111 Group Project 1

(Updated December 8, 2024)

Write a disassembler for the Commodore 64 (C64) program in the 6502/6510 assembly language.


Learning outcomes

  • Working with a translation table.
  • Working with existing data to build a new representation.
  • Working with data type manipulations.
  • Working with switch expressions.
  • Working with unsigned byte data.
  • Working with unique file formats.
  • Confirmation program produces desired results.

Historical Note
The VIC-20 only had 3,583 bytes available to programs once the system was up and running. By comparison, the Commodore 64 had 38,911 bytes.

Also, the 6502/6510 processors used were little-endian.


Setup

The file format for a C64 program is relatively simple:

Byte 0 - 1   -> load address in little-endian format.
Byte 2 - end -> program as assembled from source.

Begin by opening the file passed as an argument. Read the first two bytes to determine where in memory this program is being loaded. Beginning at the next byte, set a program counter and begin disassembling instructions.

For each instruction you will need the following table:

import java.io.FileInputStream;
import java.io.IOException;

public class Dis6502 {

    static Object[][] Opcodes = {
            /* Name, Imm,  ZP,   ZPX,  ZPY,  ABS, ABSX, ABSY,  IND, INDX, INDY, IMPL, REL */
            {"ADC", 0x69, 0x65, 0x75, null, 0x6d, 0x7d, 0x79, null, 0x61, 0x71, null, null},
            {"AND", 0x29, 0x25, 0x35, null, 0x2d, 0x3d, 0x39, null, 0x21, 0x31, null, null},
            {"ASL", null, 0x06, 0x16, null, 0x0e, 0x1e, null, null, null, null, 0x0a, null},
            {"BIT", null, 0x24, null, null, 0x2c, null, null, null, null, null, null, null},
            {"BPL", null, null, null, null, null, null, null, null, null, null, null, 0x10},
            {"BMI", null, null, null, null, null, null, null, null, null, null, null, 0x30},
            {"BVC", null, null, null, null, null, null, null, null, null, null, null, 0x50},
            {"BVS", null, null, null, null, null, null, null, null, null, null, null, 0x70},
            {"BCC", null, null, null, null, null, null, null, null, null, null, null, 0x90},
            {"BCS", null, null, null, null, null, null, null, null, null, null, null, 0xb0},
            {"BNE", null, null, null, null, null, null, null, null, null, null, null, 0xd0},
            {"BEQ", null, null, null, null, null, null, null, null, null, null, null, 0xf0},
            {"BRK", null, null, null, null, null, null, null, null, null, null, 0x00, null},
            {"CMP", 0xc9, 0xc5, 0xd5, null, 0xcd, 0xdd, 0xd9, null, 0xc1, 0xd1, null, null},
            {"CPX", 0xe0, 0xe4, null, null, 0xec, null, null, null, null, null, null, null},
            {"CPY", 0xc0, 0xc4, null, null, 0xcc, null, null, null, null, null, null, null},
            {"DEC", null, 0xc6, 0xd6, null, 0xce, 0xde, null, null, null, null, null, null},
            {"EOR", 0x49, 0x45, 0x55, null, 0x4d, 0x5d, 0x59, null, 0x41, 0x51, null, null},
            {"CLC", null, null, null, null, null, null, null, null, null, null, 0x18, null},
            {"SEC", null, null, null, null, null, null, null, null, null, null, 0x38, null},
            {"CLI", null, null, null, null, null, null, null, null, null, null, 0x58, null},
            {"SEI", null, null, null, null, null, null, null, null, null, null, 0x78, null},
            {"CLV", null, null, null, null, null, null, null, null, null, null, 0xb8, null},
            {"CLD", null, null, null, null, null, null, null, null, null, null, 0xd8, null},
            {"SED", null, null, null, null, null, null, null, null, null, null, 0xf8, null},
            {"INC", null, 0xe6, 0xf6, null, 0xee, 0xfe, null, null, null, null, null, null},
            {"JMP", null, null, null, null, 0x4c, null, null, 0x6c, null, null, null, null},
            {"JSR", null, null, null, null, 0x20, null, null, null, null, null, null, null},
            {"LDA", 0xa9, 0xa5, 0xb5, null, 0xad, 0xbd, 0xb9, null, 0xa1, 0xb1, null, null},
            {"LDX", 0xa2, 0xa6, null, 0xb6, 0xae, null, 0xbe, null, null, null, null, null},
            {"LDY", 0xa0, 0xa4, 0xb4, null, 0xac, 0xbc, null, null, null, null, null, null},
            {"LSR", null, 0x46, 0x56, null, 0x4e, 0x5e, null, null, null, null, 0x4a, null},
            {"NOP", null, null, null, null, null, null, null, null, null, null, 0xea, null},
            {"ORA", 0x09, 0x05, 0x15, null, 0x0d, 0x1d, 0x19, null, 0x01, 0x11, null, null},
            {"TAX", null, null, null, null, null, null, null, null, null, null, 0xaa, null},
            {"TXA", null, null, null, null, null, null, null, null, null, null, 0x8a, null},
            {"DEX", null, null, null, null, null, null, null, null, null, null, 0xca, null},
            {"INX", null, null, null, null, null, null, null, null, null, null, 0xe8, null},
            {"TAY", null, null, null, null, null, null, null, null, null, null, 0xa8, null},
            {"TYA", null, null, null, null, null, null, null, null, null, null, 0x98, null},
            {"DEY", null, null, null, null, null, null, null, null, null, null, 0x88, null},
            {"INY", null, null, null, null, null, null, null, null, null, null, 0xc8, null},
            {"ROR", null, 0x66, 0x76, null, 0x6e, 0x7e, null, null, null, null, 0x6a, null},
            {"ROL", null, 0x26, 0x36, null, 0x2e, 0x3e, null, null, null, null, 0x2a, null},
            {"RTI", null, null, null, null, null, null, null, null, null, null, 0x40, null},
            {"RTS", null, null, null, null, null, null, null, null, null, null, 0x60, null},
            {"SBC", 0xe9, 0xe5, 0xf5, null, 0xed, 0xfd, 0xf9, null, 0xe1, 0xf1, null, null},
            {"STA", null, 0x85, 0x95, null, 0x8d, 0x9d, 0x99, null, 0x81, 0x91, null, null},
            {"TXS", null, null, null, null, null, null, null, null, null, null, 0x9a, null},
            {"TSX", null, null, null, null, null, null, null, null, null, null, 0xba, null},
            {"PHA", null, null, null, null, null, null, null, null, null, null, 0x48, null},
            {"PLA", null, null, null, null, null, null, null, null, null, null, 0x68, null},
            {"PHP", null, null, null, null, null, null, null, null, null, null, 0x08, null},
            {"PLP", null, null, null, null, null, null, null, null, null, null, 0x28, null},
            {"STX", null, 0x86, null, 0x96, 0x8e, null, null, null, null, null, null, null},
            {"STY", null, 0x84, 0x94, null, 0x8c, null, null, null, null, null, null, null},
            {"???", null, null, null, null, null, null, null, null, null, null, null, null}
    };

    // hexdump goes here...
    private static void hexDump(byte[] bytes) {
        //...
    }

    // disassemble goes here...
    private static void disassemble (byte[] bytes) {
        // ...
    }

    public static void main(String[] args) throws IOException {

        FileInputStream in = new FileInputStream("congrats.prg");

        byte[] file = in.readAllBytes();
        in.close();

        hexDump(file);
        disassemble(file);
    }
}

The table provides each form of opcode you may encounter. Note that the two-dimensional array is of type Object. This is because we are storing multiple data types and values necessary for the success and simplicity of the program. Even though the hexadecimal values are byte-sized, they will be stored as Integer and sign-extended. Be mindful of what this means.

The number of bytes and format of each addressing mode is in the table provided below.

Addressing
   Mode       Example     Bytes
----------    -------     -----
Immediate     ORA #$44      2
Zero Page     ORA $44       2
Zero Page,X   LDA $44,X     2
Zero Page,Y   STX $44,Y     2
Absolute      ORA $4400     3
Absolute,X    ORA $4400,X   3
Absolute,Y    ORA $4400,Y   3
Indirect      JMP $4400     3
Indirect,X    ORA ($44,X)   2
Indirect,Y    ORA ($44),Y   2
Implied       INX           1
Relative      BNE $4400     2

You will perform the following using this file as your confirmation (right-click and save the file to avoid a 403 error):

  • Translate opcode.
  • Determine the addressing mode being used.
  • Convert to full opcode with proper addressing form.
  • Display each opcode with byte(s) and address.

Your output will be like the following:

3000: A9 24 85 FB A9 30 85 FC .$...0..
3008: A0 00 B1 FB F0 07 20 D2 ...... .
3010: FF C8 4C 0A 30 A2 00 BD ..L.0...
3018: 53 30 F0 07 20 D2 FF E8 S0.. ...
3020: 4C 17 30 60 20 20 20 20 L.0`    
3028: D5 C4 C4 C4 C4 C4 C4 C4 ........
3030: C4 C4 C4 C4 C4 C4 C4 C4 ........
3038: C4 C9 0D 20 20 20 20 C7 ...    .
3040: 43 4F 4E 47 52 41 54 55 CONGRATU
3048: 4C 41 54 49 4F 4E 53 21 LATIONS!
3050: C8 0D 00 20 20 20 20 C7 ...    .
3058: 20 20 20 59 4F 55 20 44    YOU D
3060: 49 44 20 49 54 21 20 20 ID IT!  
3068: C8 0D 20 20 20 20 CA C6 ..    ..
3070: C6 C6 C6 C6 C6 C6 C6 C6 ........
3078: C6 C6 C6 C6 C6 C6 C6 CB ........
3080: 0D 00                   ..

3000:   A9 24      LDA #$24
3002:   85 FB      STA $FB
3004:   A9 30      LDA #$30
3006:   85 FC      STA $FC
3008:   A0 00      LDY #$00
300A:   B1 FB      LDA ($FB),Y
300C:   F0 07      BEQ $3015
300E:   20 D2 FF   JSR $FFD2
3011:   C8         INY
3012:   4C 0A 30   JMP $300A
3015:   A2 00      LDX #$00
3017:   BD 53 30   LDA $3053,X
301A:   F0 07      BEQ $3023
301C:   20 D2 FF   JSR $FFD2
301F:   E8         INX
3020:   4C 17 30   JMP $3017
3023:   60         RTS
3024:   20 20 20   JSR $2020
3027:   20 D5 C4   JSR $C4D5
302A:   C4 C4      CPY $C4
302C:   C4 C4      CPY $C4
302E:   C4 C4      CPY $C4
3030:   C4 C4      CPY $C4
3032:   C4 C4      CPY $C4
3034:   C4 C4      CPY $C4
3036:   C4 C4      CPY $C4
3038:   C4 C9      CPY $C9
303A:   0D 20 20   ORA $2020
303D:   20 20 C7   JSR $C720
3040:   43         ???
3041:   4F         ???
3042:   4E 47 52   LSR $5247
3045:   41 54      EOR ($54,X)
3047:   55 4C      EOR $4C,X
3049:   41 54      EOR ($54,X)
304B:   49 4F      EOR #$4F
304D:   4E 53 21   LSR $2153
3050:   C8         INY
3051:   0D 00 20   ORA $2000
3054:   20 20 20   JSR $2020
3057:   C7         ???
3058:   20 20 20   JSR $2020
305B:   59 4F 55   EOR $554F,Y
305E:   20 44 49   JSR $4944
3061:   44         ???
3062:   20 49 54   JSR $5449
3065:   21 20      AND ($20,X)
3067:   20 C8 0D   JSR $0DC8
306A:   20 20 20   JSR $2020
306D:   20 CA C6   JSR $C6CA
3070:   C6 C6      DEC $C6
3072:   C6 C6      DEC $C6
3074:   C6 C6      DEC $C6
3076:   C6 C6      DEC $C6
3078:   C6 C6      DEC $C6
307A:   C6 C6      DEC $C6
307C:   C6 C6      DEC $C6
307E:   C6 CB      DEC $CB
3080:   0D         ???
3081:   00         ???

This seems like a complex project, but if you code correctly, your contribution to the solution can be as little as 120 lines of code—with comments. If you identify where cases can be combined and simplify the coding, you can reduce development time.

Your success in this project is based on the following criteria:

  • A rough outline of the problem breakdown.
  • Division of work between team members.
  • Each member provides an equal contribution to the solution.
  • A final one-page writeup of your solution with a self-assessment of what you think you did well and where you could improve the code.

Submit as Team#_Project1.java to the Learning Management System.

Post navigation

❮ Previous Post: CISS-111 Project 8
Next Post: Commodore 64 Code Examples ❯

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Copyright © 2018 – 2025 Programming by Design.