Omnimaga

General Discussion => Technology and Development => Other => Topic started by: Keoni29 on February 06, 2014, 06:23:27 pm

Title: C64 Programming Adventures
Post by: Keoni29 on February 06, 2014, 06:23:27 pm
I decided to dig up the old commodore64 and started programming it.
Projects so far:

Name: HEXEDIT
Lang: CBM BASIC
Description: Debug tool
Features:
-Edit bytes
-Memory monitor
-Save/load part of memory to/from file (60% done)
-Execute from memory address + dump cpu registers after execution
Download: (no link yet)


Name: NESC
Lang: 6502 Assembler  (+BASIC for doing something with the input)
Description: Reads buttonpresses from an NES controller hooked up to the user I/O port.
Features:
-Stores the incoming byte in $0002
-Byte can be read by a basic program like this:
Code: [Select]
10 SYS(32768)
20 PRINT PEEK(2)
30 GOTO 10
-A passive pin adapter is required in order to hook up an nes controller to the userport.
(Can be made using:userport connector,nes controller connector and a bunch of wire)
Picture of the c64's user I/O port:
(http://www.c64-wiki.com/images/thumb/f/f5/C64_Userport.jpg/300px-C64_Userport.jpg)

Download: (no link yet)
Title: Re: C64 Programming Adventures
Post by: Juju on February 06, 2014, 06:26:27 pm
Ooh, hooking up a NES controller, nice.
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 06, 2014, 06:28:15 pm
I made a basic program that does the same thing, but that was slow, so I wrote an assembly version.
Title: Re: C64 Programming Adventures
Post by: Sorunome on February 08, 2014, 03:41:25 am
nice!
Maybe I should grab a C64 out of my basement and do some stuff on it... :P
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 08, 2014, 04:43:15 am
I have been obsessed with programming the c64 for 3 days now. It almost made me fail my test because I did not study enough as a result :P I probably did allright on the test. I got an A for the first test, so I have to get an E in order to fail that course.
It reminds me of the time when I first discovered calculator programming.

My first 6502 asm program worked the first time I tested it :D I guess that's because I already know how to write eZ8 asm


Edit: Experimenting with sprites. This code will make a metroid fly across the screen. But that's not all: It also flashes bright colors.
Code: [Select]
*=$1000
;Set sprite pointer0 to $2000 ($80*$40)
        lda #$80        
        sta $07f8
;Enable sprites
        lda #$01
        sta $d015
;Expand X,Y
        sta $d01d
        sta $d017
;Set X position of sprite0 (128)
        lda #$80
        sta $d000
        sta $d001
loop
        inc $d001
        ldx #$00
delay1
        ldy #$05
delay2
        dey
        bne delay2
        dex
        bne delay1

        lda $d001
        lsr a
        lsr a
        lsr a
        lsr a
        sta $d027      

        jmp loop
        rts
Code: [Select]
*=$2000
; Metroid
 BYTE $01,$FF,$80
 BYTE $07,$FF,$E0
 BYTE $0E,$63,$F0
 BYTE $19,$99,$F8
 BYTE $33,$3D,$FC
 BYTE $37,$3D,$FC
 BYTE $66,$3C,$FE
 BYTE $68,$18,$3E
 BYTE $79,$42,$9E
 BYTE $CB,$A5,$DF
 BYTE $F3,$C3,$DF
 BYTE $F9,$E7,$BF
 BYTE $8C,$C3,$31
 BYTE $87,$18,$E1
 BYTE $59,$C3,$9A
 BYTE $3C,$FF,$3C
 BYTE $3A,$3C,$5C
 BYTE $34,$DB,$2C
 BYTE $10,$81,$08
 BYTE $08,$C3,$10
 BYTE $00,$42,$00
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 12, 2014, 04:40:54 pm
I wrote some code that shifts the entire screen one character to the right.
It takes about 1.7 seconds to shift the picture all the way to the right.



For anyone who wants to get into c64 programming: Get this book!
(http://www.commodore.ca/manuals/c64_programmers_reference/c64_prog_ref_guide.jpg)
It is full of lookup tables, datasheets and schematics!
Title: Re: C64 Programming Adventures
Post by: TIfanx1999 on February 12, 2014, 04:49:54 pm
Very cool! What language was it done with?
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 12, 2014, 04:53:42 pm
This is done in 6510 ASM. There is no way you can do it this fast in basic.

Slow and large code...
Code: [Select]
joy2      = 56320

vic       = $d000
spr0_x    = $00
spr0_y    = $01

screen0H   = $04
screen1H   = $08

ptr0      = $FC
ptr1      = $FE

defm incw
        inc /1
        bne @no_carry
        inc /1 + $01
@no_carry
        endm


*=$1000
start
         
loop
          ldx #0
          stx ptr0       ;ptr0L
          stx ptr1       ;ptr1L
          ldx #screen0H
          stx ptr0+$01   ;ptr0H
          ldx #screen1H
          stx ptr1+$01   ;ptr1H
          ldx #$08
          jsr copy_loop1
          lda #$25
          sta vic + $18
         
          ldx #0
          stx ptr0       ;ptr0L
          stx ptr1       ;ptr1L
          ldx #screen0H
          stx ptr0+$01   ;ptr0H
          ldx #screen1H
          stx ptr1+$01   ;ptr1H
          ldx #$08
          jsr copy_loop0
          lda #$15
          sta vic + $18

          jmp loop
          ;;;;;;;;;;;;;;;;
copy_loop1
                         ;Start of program.
          ldy #0             
          lda (ptr0),y       ;Copy from screen0
          incw ptr0
          ldy #1
          sta (ptr1),y     ;Save to screen1.
          incw ptr1         

          cpx ptr0 + $01  ; Stop if end of screen is reached.
          bne copy_loop1
          rts
         
copy_loop0
                         ;Start of program.
          ldy #0             
          lda (ptr1),y       ;Copy from screen0
          incw ptr1
          ldy #1
          sta (ptr0),y     ;Save to screen1.
          incw ptr0         

          cpx ptr0 + $01  ; Stop if end of screen is reached.
          bne copy_loop0
          rts
I will optimize this eventually. I want to add smooth scrolling.
Title: Re: C64 Programming Adventures
Post by: TIfanx1999 on February 12, 2014, 04:54:51 pm
Ah, ok. I wasn't sure how competent C64 BASIC was. :P

*Edit*I'm still considering getting a C64 as well as some other old computers to fool around with. ;)
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 12, 2014, 04:56:38 pm
This is a 1MHz cpu copying 1000 characters over and over again to generate the next screen. It is double buffered, so I copy the screen from one buffer to another with an offset of 1. The cpu is not fast enough to copy the entire screen in one frame, hence the double buffer. While one screen is being generated the other is being displayed by the video chip.
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 16, 2014, 08:42:59 am
I made a little test rig with a 6507 (cut down version of the 6502) that I can use for debugging pieces of code on actual hardware.
The setup is as follows:
A propeller chip is wired up to the 6507's data, address and control bus. It acts as if it were ram. It also generates the clock pulses for the 6507 cpu. The benefit over using regular ram is that I can log all reads and writes on a serial terminal on my computer.

Code for the 6507 and propeller chip in the spoiler:
Spoiler For Spoiler:
Test code listing loaded onto the propeller chip:
Code: [Select]
   $0000 a2 01     ldx #$01
    $0002 e8        inx
    $0003 86 06     stx $06
    $0005 4c 00 00  jmp $0000
This code will modify itself twice. First it will change the jump instruction so it jumps to $0002 and the next loop it changes it so it jumps to $0003. After that it will not modify the code anymore since the inx instruction is not being executed anymore.

Code for the propeller chip.
Code: [Select]
CON

  'Set up the clock mode
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000
  '5 MHz clock * 16x PLL = 80 MHz system clock speed  
VAR

  'Globally accessible variables
  byte ph0
  byte rw
  byte reset
  byte a
  byte mode
  long addr
  byte progmem[$2000]
  
OBJ

  'in leiu of Parallax Serial Terminal, FullDuplexSerial is being used to communicate with the terminal
  serial        : "FullDuplexSerial"

  
PUB Main | i
{{
  Starts execution

  This test program attempts to test all aspects of the FullDuplexSerial object.
  It executs each public method call provided by FullDuplexSerial.  Set your
  terminal to a baud rate of 9600 baud to see the output.
  
  parameters:    none
  return:        none
  
  example usage: N/A - executes on startup

}}
  ph0 := 27
  rw  := 26
  reset := 25

  mode := 0
  addr := 0
  
  dira[7..0]:=$00
  dira[20..8]:=$00
  dira[ph0]:=1
  dira[rw]:=0
  dira[reset]:=1
  outa[ph0]:=1
  outa[7..0]:=$00
  {
    $0000 a2 01      ldx #$01
    $0002 e8          inx
    $0003 86 06      stx $06
    $0005 4c 00 00  jmp $0000
  }
  progmem[$0000]:=$a2
  progmem[$0001]:=$01
  progmem[$0002]:=$e8
  progmem[$0003]:=$86
  progmem[$0004]:=$06
  progmem[$0005]:=$4c
  progmem[$0006]:=$00
  progmem[$0007]:=$00
  
  progmem[$1FFC]:=$00
  progmem[$1FFD]:=$00  
  
  'pulse reset
  outa[reset]:=0
  repeat 8
    outa[ph0]:=0
    outa[ph0]:=1
  outa[reset]:=1

  repeat 4
    Wait_1_Second

  'start the FullDuplexSerial object
  serial.Start(31, 30, %0000, 9_600)                    'requires 1 cog for operation

  'Main loop for ram emulator + control signal generator
  repeat
    'Clock low
    outa[ph0]:=0

    'Read R/W line
    if ina[rw]==0
      dira[7..0]:=0
      serial.tx(87)'w
      mode := 0
    else
      dira[7..0]:=$ff
      serial.tx(82)
      mode := 1
    
    'Display address bus value
    serial.tx(32)
    addr:=ina[20..8]
    serial.hex(addr,4)

    'Read/write byte from/to data bus
    if mode == 1
      outa[7..0] := progmem[addr]
        
    serial.tx(32)
    'Clock high
    outa[ph0]:=1
    
    if mode == 0
      progmem[addr] := ina[7..0]  
    'Look at Data bus
    serial.hex(ina[7..0],2)
    
    serial.tx(32)
    serial.tx(13)

PUB Wait_1_Second
{{
  Pauses the calling cog's execution for approximately one second.
    
  parameters:    none
  return:        none
  
  example usage: Wait_1_Second
}}

  waitcnt(cnt + clkfreq)

pub Wait_100_Ms
  waitcnt(clkfreq/10 + cnt)

Edit: I am reworking the code a bit so you can control everything from a serial terminal. After a reset the propeller chip waits for the pc to send it a program. You can just send a binary file using realterm and it will work. Note that it puts the program at 0002 (the reset vector is also 0002.) I will probably write a program for windows that allows you to send programs by just dragging them over the icon.
Title: Re: C64 Programming Adventures
Post by: Keoni29 on February 17, 2014, 04:32:21 pm
Added features:
- 4 bit I/O port with programmable data direction.
   $0000 [3..0] Data direction register
   $0001 [3..0] Input/Output register
-You can send binaries via realterm.

New example code: Toggles an LED on the I/O port
Code: [Select]
*=2
lda #01
sta $00

loop0:
lda $01
and #2
beq loop0:

loop1:
lda $01
and #2
bne loop1:

lda $01
and #1
eor #1
sta $01

jmp loop0:
.end
Title: Re: C64 Programming Adventures
Post by: Keoni29 on July 09, 2014, 05:33:36 pm
Yet another member owns a commodore 64 now. I found a whole stack of commodore stuff in a thriftshop including 3 disk drives, 2 cartridges, a datasette and ofc. a commodore 64.  aeTIos came over to my place today and came to pick it up. Expect more software to be released soon!


Left: me, right: aeTIos.
Title: Re: C64 Programming Adventures
Post by: Streetwalrus on July 09, 2014, 05:36:51 pm
Cool ! :D Also, you both need to grow some hair. :P
Title: Re: C64 Programming Adventures
Post by: DJ Omnimaga on July 09, 2014, 11:40:03 pm
Cool ! :D Also, you both need to grow some hair. :P
Well, not everyone necessarily wants 12 inch long hairs like us, though :P


By the way Keoni29 do you think Reuben Quest: Ev Awakening or Supersonic Ball would be feasible on the C64? :P
Title: Re: C64 Programming Adventures
Post by: Streetwalrus on July 10, 2014, 05:36:15 am
According to Kiwidepia it's a 6510 at about 1MHz so I don't think it's possible. :P
Title: Re: C64 Programming Adventures
Post by: Keoni29 on July 10, 2014, 06:13:09 am
And I'm not that good at 6502 assembly yet. The most complex thing I made so far is a sprite which you can move using the joystick.
Title: Re: C64 Programming Adventures
Post by: TIfanx1999 on July 10, 2014, 07:46:22 am
Yet another member owns a commodore 64 now. I found a whole stack of commodore stuff in a thriftshop including 3 disk drives, 2 cartridges, a datasette and ofc. a commodore 64.  aeTIos came over to my place today and came to pick it up. Expect more software to be released soon!


Left: me, right: aeTIos.

That's awesome that you guys were able to meet up. ;D Congrats on getting a c64 AeTIos. ;D
Title: Re: C64 Programming Adventures
Post by: DJ Omnimaga on July 10, 2014, 12:23:24 pm
And I'm not that good at 6502 assembly yet. The most complex thing I made so far is a sprite which you can move using the joystick.
would it be possible in BASIC, though, even if the game lacks scrolling and the character moves block by block like in the original 2004 game?
Title: Re: C64 Programming Adventures
Post by: Keoni29 on July 10, 2014, 12:48:16 pm
Simple interrupt-driven border flasher:
Code: [Select]
setup=*
sei
lda #<intcode
sta 788
lda #>intcode
sta 789
cli
rts
intcode=*
inc $d020
jmp $ea31
Hex:
Code: [Select]
78 A9 0D 8D 14 03 A9 10
8D 15 03 58 60 EE 20 D0
4C 31 EA




Read NES controller on userport (special hardware required)
Hex only for now...
Result is stored at $0002
Code: [Select]

A9 01 85 FB A9 06 8D 03
DD A9 02 8D 01 DD A2 08
A9 00 8D 01 DD 06 02 AD
01 DD A0 04 8C 01 DD 25
FB 05 02 85 02 A0 00 8C
01 DD CA D0 E8 60
Title: Re: C64 Programming Adventures
Post by: aeTIos on July 11, 2014, 10:46:07 am
Found an error in my c64 manual.
it tells me to type in this:

1 REM UP, UP, AND AWAY
5 PRINT "(CLR/HOME)"
10 V = 53248 : REM START OF DISPLAY CHIP
11 POKE V + 21,4 : REM ENABLE SPRITE 2
12 POKE 2042,255 : REM SPRITE 2 DATA FROM BLOCK 255
20 FOR N = 0 TO 62 : READ Q : POKE 832+N,Q : NEXT
30 FOR X = 0 TO 200     ^ GETS ITS INFO. FROM DATA
40 POKE V + 4,X : REM UPDATE X COORDINATES
50 POKE V + 5,X : REM UPDATE Y COORDINATES
60 NEXT X
70 GOTO 30
200 DATA 0,127,0,1,255,192,3,255,224,3,231,224
210 DATA 7,217,240,7,223,240,7,217,240,3,231,224
220 DATA 3,255,224,3,255,224,2,255,160,1,127,64
230 DATA 1,62,64,0,156,128,0,156,128,0,73,0,0,73,0
240 DATA 0,62,0,0,62,0,0,62,0,0,28,0


So I typed that in and got a garbled sprite. Did some research, discovered that the BLOCK should be [buffloc]/64 and thus 13, and not 255.
*phew* I thought it was broken for a sec :P
Title: Re: C64 Programming Adventures
Post by: Keoni29 on July 12, 2014, 10:02:39 am
Program for drawing blocks on the screen:
Code: [Select]
// Draw blocks on screen
// Author:      Koen van Vliet
// Date:      12 Jul 2014
//
// Notes:
// *   Use kickassembler to assemble

//=================================================
// B A S I C  S Y S ()
//=================================================
.pc =$0801 "Basic Upstart Program"
:BasicUpstart(main)
//=================================================
// M A I N
//=================================================
.pc = $0820
main:
      jsr $E544            // Kernal routine: Clear screen
      sei                  // Disable interrupts
      // Black block
      :fillBlock($0400,4,16,10,6,35)
      :fillBlock($D800,4,16,10,6,0)
      // Green block
      :fillBlock($0400,11,1,18,23,224)
      :fillBlock($D800,11,1,18,23,13)
      // White block
      :fillBlock($0400,16,6,20,6,0)
      :fillBlock($D800,16,6,20,6,1)
      cli                  // Enable interrupts
      rts                  // Return to Basic
//=================================================
// M A C R O 'S
//=================================================
// Routine: fillBlock
//       Fills a rectangular block in memory with one value
.macro fillBlock(base,col,row,w,h,val){
      .const addr = base + 40 * row + col - 1
      lda #<addr            // Set up parameters         
      sta mod_b_addr + 1
      lda #>addr         
      sta mod_b_addr + 2
      lda #w
      sta mod_b_w + 1
      lda #h
      sta mod_b_h + 1
      lda #val
      sta mod_b_v + 1
      jsr fill_block         // Draw a block on screen
}
//=================================================
// R O U T I N E S
//=================================================
// Routine: fill block
//       Fills a rectangular block in memory with one value
//
// Input: Modify parameters by modifying operands.
//         Preferably use the macro for this
// Output: -
// Notes:
// *   Intended for filling blocks in screen/color ram.
//      Assumes 40 bytes per row.
// *   Does not clip outside of screen area.
fill_block:
   mod_b_h:
      ldy #$08            // Operand is modified by program.
loop_row:                  // Default value given to make this
   mod_b_w:
      ldx #$08            // Operand is modified by program.
loop_col:
   mod_b_v:
      lda #224            // Fill block with single value.
   mod_b_addr:               // Operand is modified by program.
      sta $FFFF,x             // Write character to screen. Operand is
      dex                  // modified by program.
      bne loop_col

      clc
      lda #40
      adc mod_b_addr + 1      // Modify operand low byte
      sta mod_b_addr + 1
      lda #0
      adc mod_b_addr + 2      // Modify operand high byte
      sta mod_b_addr + 2

      dey
      bne loop_row
      rts


You can draw blocks at any memory location. You must specify the base address  of the ram area you want to use.
To draw a block of characters on the screen the base address is $0400
To change the color you simply fill a block in color ram. The base address for color ram is $D800