~rabbits/spacetime

dc9272d6d446ccb09885839c00b1cfaec0b6f159 — Devine Lu Linvega 3 years ago 488f7de
Updated lin6
5 files changed, 511 insertions(+), 530 deletions(-)

M build.sh
M src/cart.asm
D src/sprite.chr
M tools/lin6
M tools/lin6.c
M build.sh => build.sh +2 -4
@@ 23,10 23,8 @@ else
fi

# Lint project
for filename in src/*.asm; do
    [ -e "$filename" ] || continue
   	tools/lin6 -i "$filename"
done
tools/lin6 src/cart.asm > linted.asm
mv linted.asm src/cart.asm

# Build new cart
echo "Assembling new cart."

M src/cart.asm => src/cart.asm +172 -172
@@ 4,83 4,83 @@
	.db "NES", $1a ; identification of the iNES header
	.db 1 ; number of 16KB PRG-ROM pages
	.db $01 ; number of 8KB CHR-ROM pages
	.db $70|%0001 ; mapper 7
	.dsb $09, $00 ; clear the remaining bytes
	.db $00 ; NROM
	.dsb $09,$00 ; clear the remaining bytes
	.fillvalue $FF ; Sets all unused space in rom to value $FF
	.org $C000

;; constants

PPUCTRL             .equ $2000 
PPUMASK             .equ $2001 
PPUSTATUS           .equ $2002 
SPRADDR             .equ $2003 
PPUSCROLL           .equ $2005 
PPUADDR             .equ $2006 
PPUDATA             .equ $2007 
APUCH1VOL           .equ $4000 ; APU
APUCH1SWP           .equ $4001 ; APU
APUCH1FRQ           .equ $4002 ; APU
APUCH1LEN           .equ $4003 ; APU
APUCH2VOL           .equ $4004 ; APU
APUCH2SWP           .equ $4005 ; APU
APUCH2FRQ           .equ $4006 ; APU
APUCH2LEN           .equ $4007 ; APU
APUCH3CNT           .equ $4008 ; APU
APUCH3SWP           .equ $4009 ; APU
APUCH3FRQ           .equ $400a ; APU
APUCH3LEN           .equ $400b ; APU
APUCH4VOL           .equ $400c ; APU
APUCH4SWP           .equ $400d ; APU
APUCH4FRQ           .equ $400e ; APU
APUCH4LEN           .equ $400f ; APU
SPRDMA              .equ $4014 
APUCTRL             .equ $4015 
JOY1                .equ $4016 
JOY2                .equ $4017 
BUTTON_RIGHT        .equ #$01 
BUTTON_LEFT         .equ #$02 
BUTTON_DOWN         .equ #$04 
BUTTON_UP           .equ #$08 
BUTTON_START        .equ #$10 
BUTTON_SELECT       .equ #$20 
BUTTON_B            .equ #$40 
BUTTON_A            .equ #$80 
	PPUCTRL                 .equ $2000 
	PPUMASK                 .equ $2001 
	PPUSTATUS               .equ $2002 
	SPRADDR                 .equ $2003 
	PPUSCROLL               .equ $2005 
	PPUADDR                 .equ $2006 
	PPUDATA                 .equ $2007 
	APUCH1VOL               .equ $4000 ; APU
	APUCH1SWP               .equ $4001 ; APU
	APUCH1FRQ               .equ $4002 ; APU
	APUCH1LEN               .equ $4003 ; APU
	APUCH2VOL               .equ $4004 ; APU
	APUCH2SWP               .equ $4005 ; APU
	APUCH2FRQ               .equ $4006 ; APU
	APUCH2LEN               .equ $4007 ; APU
	APUCH3CNT               .equ $4008 ; APU
	APUCH3SWP               .equ $4009 ; APU
	APUCH3FRQ               .equ $400a ; APU
	APUCH3LEN               .equ $400b ; APU
	APUCH4VOL               .equ $400c ; APU
	APUCH4SWP               .equ $400d ; APU
	APUCH4FRQ               .equ $400e ; APU
	APUCH4LEN               .equ $400f ; APU
	SPRDMA                  .equ $4014 
	APUCTRL                 .equ $4015 
	JOY1                    .equ $4016 
	JOY2                    .equ $4017 
	BUTTON_RIGHT            .equ #$01  
	BUTTON_LEFT             .equ #$02  
	BUTTON_DOWN             .equ #$04  
	BUTTON_UP               .equ #$08  
	BUTTON_START            .equ #$10  
	BUTTON_SELECT           .equ #$20  
	BUTTON_B                .equ #$40  
	BUTTON_A                .equ #$80  

;; variables

	.enum $0000
position                .dsb 1 
cursor                  .dsb 1 
note                    .dsb 1 
marker                  .dsb 1 
btndown                 .dsb 1 
btnlast                 .dsb 1 
btnqueue                .dsb 1 
rate                    .dsb 1 
timer                   .dsb 1 
seed                    .dsb 1 
	position                .dsb 1
	cursor                  .dsb 1
	note                    .dsb 1
	marker                  .dsb 1
	btndown                 .dsb 1
	btnlast                 .dsb 1
	btnqueue                .dsb 1
	rate                    .dsb 1
	timer                   .dsb 1
	seed                    .dsb 1
	.ende 

;; reset

RESET:
	SEI                          ; disable IRQs
	CLD                          ; disable decimal mode
RESET: 
	SEI                                ; disable IRQs
	CLD                                ; disable decimal mode
	LDX #$40
	STX $4017                    ; disable APU frame IRQ
	STX $4017                          ; disable APU frame IRQ
	LDX #$FF
	TXS                          ; set up stack
	INX                          ; now X = 0
	STX PPUCTRL                  ; disable NMI
	STX PPUMASK                  ; disable rendering
	STX $4010                    ; disable DMC IRQs
	TXS                                ; set up stack
	INX                                ; now X = 0
	STX PPUCTRL                        ; disable NMI
	STX PPUMASK                        ; disable rendering
	STX $4010                          ; disable DMC IRQs

Vblankwait1: ; First wait for vblank
	BIT PPUSTATUS
	BPL Vblankwait1

Clrmem:
Clrmem: 
	LDA #$00
	STA $0000, x
	STA $0100, x


@@ 90,7 90,7 @@ Clrmem:
	STA $0600, x
	STA $0700, x
	LDA #$FE
	STA $0200, x                 ; move sprites off screen
	STA $0200, x                       ; move sprites off screen
	INX 
	BNE Clrmem



@@ 100,90 100,90 @@ Vblankwait2: ; Second wait for vblank

;; setup phase

SetupPalettes:
SetupPalettes: 
	LDA PPUSTATUS
	LDA #$3F
	STA PPUADDR
	LDA #$00
	STA PPUADDR
	LDX #$00
@loop:
@loop: 
	LDA Palettes, x
	STA PPUDATA
	INX 
	CPX #$20
	BNE @loop

SetupAttributes:
SetupAttributes: 
	LDA PPUSTATUS
	LDA #$23
	STA PPUADDR
	LDA #$C0
	STA PPUADDR
	LDX #$00
@loop:
@loop: 
	LDA #$00
	STA PPUDATA
	INX 
	CPX #$40
	BNE @loop

SetupSequence:
SetupSequence: 
	LDX #$00
@loop:
	LDA $00                      ; set entire sequence to "+"
	STA $10,x                    ; store on zeropage second row
@loop: 
	LDA $00                            ; set entire sequence to "+"
	STA $10,x                          ; store on zeropage second row
	INX 
	CPX #$10
	BNE @loop

SetupVariables:
SetupVariables: 
	LDA #$08
	STA rate                     ; default rate = 08
	STA rate                           ; default rate = 08
	LDA #$20
	STA note                     ; default note = C
	STA note                           ; default note = C
	LDA #$05
	STA marker

SetupAudio:
	LDY #$0f                     ; init $4000-4013
@loop:
SetupAudio: 
	LDY #$0f                           ; init $4000-4013
@loop: 
	LDA ApuStates,y
	STA $4000,y
	DEY 
	BPL @loop
	LDA #$0f                     ; skip over $4014 (OAMDMA)
	LDA #$0f                           ; skip over $4014 (OAMDMA)
	STA $4015
	LDA #$40
	STA $4017

SetupInterface:
@cursor:
SetupInterface: 
@cursor: 
	LDA #$88
	STA $0200                    ; set tile.y pos
	STA $0200                          ; set tile.y pos
	LDA #$02
	STA $0201                    ; set tile.id
	STA $0201                          ; set tile.id
	LDA #$00
	STA $0202                    ; set tile.attribute
@marker:
	STA $0202                          ; set tile.attribute
@marker: 
	LDA #$88
	STA $0204                    ; set tile.y pos
	STA $0204                          ; set tile.y pos
	LDA #$03
	STA $0205                    ; set tile.id
	STA $0205                          ; set tile.id
	LDA #$00
	STA $0206                    ; set tile.attribute
	STA $0206                          ; set tile.attribute

EnableSprites:
	LDA #%10000000               ; enableNMI sprites0 background0
EnableSprites: 
	LDA #%10000000                     ; enableNMI sprites0 background0
	STA PPUCTRL
	LDA #%00011110               ; sprites background noclip
	LDA #%00011110                     ; sprites background noclip
	STA PPUMASK

;; main phase

MAIN:
MAIN: 
	LDA btnqueue
@style1:
@style1: 
	CMP BUTTON_LEFT
	BEQ MoveLeft
	CMP BUTTON_RIGHT


@@ 192,7 192,7 @@ MAIN:
	BEQ MoveUp
	CMP BUTTON_DOWN
	BEQ MoveDown
@style2:
@style2: 
	CMP BUTTON_SELECT
	BEQ ResetSequence
	CMP BUTTON_START


@@ 201,69 201,69 @@ MAIN:
	BEQ MoveUp
	CMP BUTTON_B
	BEQ MoveDown
	JMP MAIN                     ; infinite loop
	JMP MAIN                           ; infinite loop

;; General functions

MoveLeft:
MoveLeft: 
	DEC cursor
	LDA cursor
	CMP #$ff
	BNE @done                    ; when equal skip
	LDA #$0f                     ; wrap around
	BNE @done                          ; when equal skip
	LDA #$0f                           ; wrap around
	STA cursor
@done:
@done: 
	JMP MoveEnd

MoveRight:
MoveRight: 
	INC cursor
	LDA cursor
	CMP #$10
	BCC @done                    ; when greaterthan skip
	LDA #$00                     ; wrap around
	BCC @done                          ; when greaterthan skip
	LDA #$00                           ; wrap around
	STA cursor
@done:
@done: 
	JMP MoveEnd

MoveDown:
MoveDown: 
	LDX cursor
	DEC $10,x
	LDA $10,x
	CMP #$ff                     ; when equal skip
	BNE @done                    ; wrap around
	CMP #$ff                           ; when equal skip
	BNE @done                          ; wrap around
	LDA #$07
	STA $10,x
@done:
@done: 
	JMP MoveEnd

MoveUp:
MoveUp: 
	LDX cursor
	INC $10,x
	LDA $10,x
	CMP #$08                     ; when greaterthan skip
	BCC @done                    ; wrap around
	CMP #$08                           ; when greaterthan skip
	BCC @done                          ; wrap around
	LDA #$00
	STA $10,x
@done:
@done: 
	JMP MoveEnd

MoveMarker:
MoveMarker: 
	LDA cursor
	STA marker
	STA RedrawInterface
	JMP MoveEnd

ResetSequence:
ResetSequence: 
	LDX #$00
@loop:
	LDA #$01                     ; set entire sequence to "+"
	STA $10,x                    ; store on zeropage second row
@loop: 
	LDA #$01                           ; set entire sequence to "+"
	STA $10,x                          ; store on zeropage second row
	INX 
	CPX #$10
	BNE @loop
	JMP MoveEnd

MoveEnd:
MoveEnd: 
	LDA #$00
	STA btnqueue
	STA RedrawTimeline


@@ 271,30 271,30 @@ MoveEnd:

;; interface

RedrawTimeline:
RedrawTimeline: 
	LDX #$00
@loop:
	LDA #$22                     ; sprite v pos
@loop: 
	LDA #$22                           ; sprite v pos
	STA PPUADDR
	TXA                          ; sprite x pos
	TXA                                ; sprite x pos
	STA PPUADDR
	LDA #$10, x                  ; find selection
	LDA #$10, x                        ; find selection
	CLC 
	ADC #$10
	CPX cursor                   ; if cursor
	CPX cursor                         ; if cursor
	BNE @continue
	CLC 
	ADC #$08                     ; then, highlight
@continue:
	STA PPUDATA                  ; sprite id
	ADC #$08                           ; then, highlight
@continue: 
	STA PPUDATA                        ; sprite id
	INX 
	CPX #$10
	BNE @loop
	RTS 

RedrawInterface:
RedrawInterface: 
	LDA PPUSTATUS
@selection:
@selection: 
	LDA cursor
	CLC 
	ADC #$20


@@ 303,7 303,7 @@ RedrawInterface:
	STY PPUADDR
	STX PPUADDR
	STA PPUDATA
@marker:
@marker: 
	LDA marker
	CLC 
	ADC #$20


@@ 312,7 312,7 @@ RedrawInterface:
	STY PPUADDR
	STX PPUADDR
	STA PPUDATA
@position:
@position: 
	LDA position
	CLC 
	ADC #$20


@@ 321,7 321,7 @@ RedrawInterface:
	STY PPUADDR
	STX PPUADDR
	STA PPUDATA
@rate:
@rate: 
	LDA rate
	CLC 
	ADC #$20


@@ 330,45 330,45 @@ RedrawInterface:
	STY PPUADDR
	STX PPUADDR
	STA PPUDATA
@sprites:
@sprites: 
	LDA position
	ASL 
	ASL 
	ASL 
	CLC 
	ADC #$40
	STA $0203                    ; set tile.x pos
	STA $0203                          ; set tile.x pos
	LDA marker
	ASL 
	ASL 
	ASL 
	CLC 
	ADC #$40
	STA $0207                    ; set tile.x pos
@done:
	STA $0207                          ; set tile.x pos
@done: 
	RTS 

IncNote:
IncNote: 
	INC note
	LDA note
	CMP #$48
	BNE @done
	LDA #$00                     ; when reached end
	STA note                     ; reset note
@done:
	LDA #$00                           ; when reached end
	STA note                           ; reset note
@done: 
	RTS 

DecNote:
DecNote: 
	DEC note
	LDA note
	CMP #$ff
	BNE @done
	LDA #$47
	STA note
@done:
@done: 
	RTS 

PlaySnare:
PlaySnare: 
	LDA #<$3f8
	STA APUCH4LEN
	LDA #>$3f8


@@ 379,37 379,37 @@ PlaySnare:

;; operators

DoInc:
DoInc: 
	LDX #$00
@loop:
@loop: 
	JSR IncNote
	CPX marker
	INX 
	BCC @loop
	JMP DoOperationEnd

DoDec:
DoDec: 
	LDX #$00
@loop:
@loop: 
	JSR DecNote
	CPX marker
	INX 
	BCC @loop
	JMP DoOperationEnd

DoTop:
DoTop: 
	LDA #$24
	STA note
	JMP DoOperationEnd

DoBottom:
DoBottom: 
	LDA #$00
	STA note
	JMP DoOperationEnd

DoRandomNote:
DoRandomNote: 
	LDA seed
@loop:
@loop: 
	SEC 
	SBC #$48
	CMP #$48


@@ 419,35 419,35 @@ DoRandomNote:
	JSR PlaySnare
	JMP DoOperationEnd

DoIncMetro:
DoIncMetro: 
	INC rate
	LDA rate
	CMP #$0c
	BCC @done
	LDA #$01
	STA rate
@done:
@done: 
	JMP DoOperationEnd

DoDecMetro:
DoDecMetro: 
	DEC rate
	LDA rate
	CMP #$00
	BNE @done
	LDA #$0c
	STA rate
@done:
@done: 
	JMP DoOperationEnd

DoRandomPosition:
DoRandomPosition: 
	LDA marker
	STA position
	JMP DoOperationEnd

DoOperation:
DoOperation: 
	LDX position
	LDA $10,x
	CMP #$00                     ;
	CMP #$00
	BEQ DoInc
	CMP #$01
	BEQ DoDec


@@ 467,23 467,23 @@ DoOperation:
DoOperationEnd: ; return from operations
	RTS 

DoSound:
DoSound: 
	LDX note
@ch1:
@ch1: 
	LDA NotesHi,x
	STA APUCH1LEN
	LDA NotesLo,x
	STA APUCH1FRQ
	LDY position
	STY APUCH1SWP
@ch2:
@ch2: 
	LDA NotesHi,x
	STA APUCH2LEN
	LDA NotesLo,x
	STA APUCH2FRQ
	LDY marker
	STY APUCH2SWP
@ch3:
@ch3: 
	LDA NotesHi,x
	STA APUCH3LEN
	LDA NotesLo,x


@@ 496,19 496,19 @@ DoSound:

NMI: ;

ReadJoy:
ReadJoy: 
	LDA #$01
	STA JOY1                     ; start reading
	STA JOY1                           ; start reading
	STA btndown
	LSR a
	STA JOY1
@loop:
@loop: 
	LDA JOY1
	LSR a
	ROL btndown
	BCC @loop

SaveJoy:
SaveJoy: 
	LDA btndown
	CMP btnlast
	BEQ @done


@@ 516,39 516,39 @@ SaveJoy:
	CMP #$00
	BEQ @done
	STA btnqueue
@done:
@done: 
	LDA #$00
	STA SPRADDR
	LDA #$02
	STA $4014

RunTimer:
RunTimer: 
	INC seed
	INC timer
	LDA timer
	CMP rate                     ; check if timer has reached rate
	BNE @done                    ; skip if timer is less than rate
	CMP rate                           ; check if timer has reached rate
	BNE @done                          ; skip if timer is less than rate
	LDA #$00
	STA timer                    ; reset timer
	STA timer                          ; reset timer
	JSR DoOperation
	JSR DoSound
	INC position
	LDA position
	CMP #$10                     ; check if position has reached 16
	CMP #$10                           ; check if position has reached 16
	BNE @done
	LDA #$00
	STA position
@done:
@done: 

Cleanup:
Cleanup: 
	JSR RedrawTimeline
	JSR RedrawInterface
	LDA PPUSTATUS
	LDA #$c0                     ; shift background to center
	LDA #$c0                           ; shift background to center
	STA PPUSCROLL
	LDA #$00
	STA PPUSCROLL
	RTI                          ; return from interrupt
	RTI                                ; return from interrupt

;; tables



@@ 558,13 558,13 @@ Palettes: ;
	.db $0F,$3B,$16,$30, $3B,$3B,$0F,$0F, $0F,$3B,$0F,$0F, $0F,$3B,$0F,$0F
	.db $0F,$3B,$16,$3B, $0F,$3B,$0F,$0F, $0F,$0F,$0F,$0F, $0F,$0F,$0F,$0F

ApuStates:
ApuStates: 
	.db #%10100111,$08,$00,$00
	.db #%00011101,$08,$00,$00
	.db #%01011101,$08,$00,$00
	.db #%01011011,$08,$00,$00

NotesHi:
NotesHi: 
	.db $07,$07,$07,$06,$06,$05,$05,$05,$05,$04,$04,$04
	.db $03,$03,$03,$03,$03,$02,$02,$02,$02,$02,$02,$02
	.db $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01


@@ 572,7 572,7 @@ NotesHi:
	.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00

NotesLo:
NotesLo: 
	.db $f1,$7f,$13,$ad,$4d,$f3,$9d,$4c,$00,$b8,$74,$34
	.db $f8,$bf,$89,$56,$26,$f9,$ce,$a6,$80,$5c,$3a,$1a
	.db $fb,$df,$c4,$ab,$93,$7c,$67,$52,$3f,$2d,$1c,$0c

D src/sprite.chr => src/sprite.chr +0 -0
M tools/lin6 => tools/lin6 +0 -0
M tools/lin6.c => tools/lin6.c +337 -354
@@ 1,450 1,433 @@
/* Lin6
  Version 1.2
/* 
  Lin6
  Version 1.3
  https://wiki.xxiivv.com/lin6
*/

/* Rules:
  Variable names must always be lowercase.
  Variable names are always padded to tab1.
  Variable length are always padded to col25.
  Variable comments are always padded to col32.
  Constant names must always be uppercase.
  Constant names are always padded to tab1.
  Constant length are always padded to col21.
  Constant comments are always padded to col32.
  Label names must always be capitalized.
  Label names aalways end with :.
  Label names always end with :.
  Label names are always preceeded with a linebreak.
  Label comments are never padded.
  Directive names are always padded to col2.
  Directive names are always padded to tab1.
  Directive names are always lowercase.
  Directive comments are never padded.
  Opcode names are always uppercase.
  Opcode names are always padded to col2.
  Opcode names are always padded to tab1.
  Opcode comments are always padded to col32.
  Inline comments are always padded to col2.
  Inline comments are always padded to tab1.
  Spacing comments are always preceeded and followed with a linebreak.
*/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VERSION "1.2"
#define VERSION "1.3"
#define BUFLEN 512

typedef enum { false, true } bool;
char* OPCODES[] = {"ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI",
           "BNE", "BPL", "BRK", "BVC", "BVS", "CLC", "CLD", "CLI",
           "CLV", "CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR",
           "INC", "INX", "INY", "JMP", "JSR", "LDA", "LDX", "LDY",
           "LSR", "NOP", "ORA", "PHA", "PHP", "PLA", "PLP", "ROL",
           "ROR", "RTI", "RTS", "SBC", "SEC", "SED", "SEI", "STA",
           "STX", "STY", "TAX", "TAY", "TSX", "TXA", "TXS", "TYA"};

char *OPCODES[] = {"ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI",
                   "BNE", "BPL", "BRK", "BVC", "BVS", "CLC", "CLD", "CLI",
                   "CLV", "CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR",
                   "INC", "INX", "INY", "JMP", "JSR", "LDA", "LDX", "LDY",
                   "LSR", "NOP", "ORA", "PHA", "PHP", "PLA", "PLP", "ROL",
                   "ROR", "RTI", "RTS", "SBC", "SEC", "SED", "SEI", "STA",
                   "STX", "STY", "TAX", "TAY", "TSX", "TXA", "TXS", "TYA"};
int skipped = 0;

void substr(char *src, char *dest, int from, int to) {
  memcpy(dest, src + from, to);
  dest[to] = '\0';
int
cisp(char c)
{
  return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

void trimstr(char *src, char *dest) {
  /* leading whitespace */
  int a, b, len = strlen(src) + 1;
  for (a = 0; a < len; a++) {
    if (src[a] != ' ' && src[a] != '\t') {
      break;
    }
  }
  /* traling whitespace */
  for (b = 2; b < len; b++) {
    if (src[len - b] != ' ' && src[len - b] != '\n') {
      break;
    }
  }
  substr(src, dest, a, len - b - a + 1);
int
clca(int c)
{
  return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
}

void fillstr(char *src, int limit, char c) {
  int i, len = strlen(src);
  for (i = len; i < limit; i++) {
    src[i] = c;
  }
  src[limit] = '\0';
int
cuca(char c)
{
  return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c;
}

void swapcstr(char *src, char a, char b) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    src[i] = src[i] == a ? b : src[i];
  }
int
ciuc(char c)
{
  return c == cuca(c);
}

void cpystr(char *src, char *dest) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    dest[i] = src[i];
  }
  dest[len] = '\0';
int
cilc(char c)
{
  return c == clca(c);
}

void ucstr(char *src) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    src[i] = toupper(src[i]);
  }
char*
sstr(char* src, char* dest, int from, int to)
{
  int i;
  char *a = (char*)src + from, *b = (char*)dest;
  for(i = 0; i < to; i++)
    b[i] = a[i];
  dest[to] = '\0';
  return dest;
}

void lcstr(char *src) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    src[i] = tolower(src[i]);
  }
int
slen(char* s)
{
  int n = 0;
  while(s[n] != '\0' && s[++n])
    ;
  return n;
}

int
silc(char* s)
{
  int i;
  for(i = 0; i < slen(s); i++)
    if(!cilc(s[i]))
      return 0;
  return 1;
}

int
siuc(char* s)
{
  int i;
  for(i = 0; i < slen(s); i++)
    if(!ciuc(s[i]))
      return 0;
  return 1;
}

int index_of_comment(char *src) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    if (src[i] == ';') {
int
cpos(char* s, char c)
{
  int i;
  for(i = 0; i < slen(s); i++)
    if(s[i] == c)
      return i;
    }
  }
  return -1;
}

bool opcode_exists(char *opcode) {
  int i;
  for (i = 0; i < 56; i++) {
    if (strcmp(OPCODES[i], opcode) == 0) {
      return true;
    }
  }
  return false;
char*
scat(char* dest, const char* src)
{
  char* ptr = dest + slen(dest);
  while(*src != '\0')
    *ptr++ = *src++;
  *ptr = '\0';
  return dest;
}

bool file_exists(char *filename) {
  FILE *file = fopen(filename, "r");
  if (file != NULL) {
    fclose(file);
    return true;
  }
  return false;
int
scmp(char* a, char* b)
{
  int i, l = slen(a);
  if(l != slen(b))
    return 0;
  for(i = 0; i < l; ++i)
    if(a[i] != b[i])
      return 0;
  return 1;
}

void set_line_key(char *src, char *dest) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    if (src[i] == ' ') {
      break;
    }
  }
  substr(src, dest, 0, i);
  trimstr(dest, dest);
char*
suca(char* s)
{
  int i;
  for(i = 0; i < slen(s); i++)
    s[i] = cuca(s[i]);
  return s;
}

void set_line_value(char *src, char *dest) {
  bool step = false;
  int i, comment = index_of_comment(src), len = strlen(src);
  if (comment > -1) {
    len = comment;
  }
  for (i = 0; i < len; i++) {
    if (src[i] != ' ' && step == true) {
      break;
    } else if (src[i] == ' ') {
      step = true;
    }
  }
  substr(src, dest, i, len - i);
  trimstr(dest, dest);
char*
slca(char* s)
{
  int i;
  for(i = 0; i < slen(s); i++)
    s[i] = clca(s[i]);
  return s;
}

void set_line_comment(char *src, char *dest) {
  int i, len = strlen(src);
  for (i = 0; i < len; i++) {
    if (src[i] == ';') {
      break;
    }
  }
  substr(src, dest, i, len - i);
  trimstr(dest, dest);
char*
strm(char* s)
{
  char* end;
  while(cisp(*s))
    s++;
  if(*s == 0)
    return s;
  end = s + slen(s) - 1;
  while(end > s && cisp(*end))
    end--;
  end[1] = '\0';
  return s;
}

bool has_comment(char *line) {
  int i, len = strlen(line);
  for (i = 0; i < len; i++) {
    if (line[i] == ';') {
      return true;
int
spos(char* a, char* b)
{
  int i, j, alen = slen(a), blen = slen(b);
  for(i = 0; i < alen; i++) {
    for(j = 0; j < blen; j++) {
      if(a[i + j] == '\0')
        return -1;
      if(a[i + j] != b[j])
        break;
      if(j == blen - 1)
        return i;
    }
  }
  return false;
  return -1;
}

bool is_capital(char *str) { return str[0] == toupper(str[0]); }
char*
scpy(char* src, char* dest)
{
  int i = 0;
  while((dest[i] = src[i]) != '\0')
    i++;
  return dest;
}

bool is_lower(char *str) {
  int i, len = strlen(str);
  for (i = 0; i < len; i++) {
    if (str[i] != tolower(str[i])) {
      return false;
    }
  }
  return true;
void
setkey(char* src, char* dst)
{
  sstr(src, dst, 0, cpos(src, ' ') > 0 ? cpos(src, ' ') : slen(src));
}

bool is_upper(char *str) {
  int i, len = strlen(str);
  for (i = 0; i < len; i++) {
    if (str[i] != toupper(str[i])) {
      return false;
    }
void
setval(char* src, char* dst)
{
  int step = 0, i, comment = cpos(src, ';'), len = slen(src);
  if(comment > -1)
    len = comment;
  for(i = 0; i < len; i++) {
    if(src[i] != ' ' && step == 1)
      break;
    else if(src[i] == ' ')
      step = 1;
  }
  return true;
  sstr(src, dst, i, len - i);
  scpy(strm(dst), dst);
}

bool is_label(char *line) {
  int i, len = strlen(line);
  for (i = 0; i < len; i++) {
    if (line[i] == ' ' || line[i] == ';') {
      return false;
    }
    if (line[i] == ':') {
      return true;
    }
void
setcmt(char* src, char* dst)
{
  if(cpos(src, ';') > 0)
    sstr(src, dst, cpos(src, ';'), slen(src) - cpos(src, ';'));
  else
    scpy("", dst);
  scpy(strm(dst), dst);
}

int
islabel(char* line)
{
  int i;
  for(i = 0; i < slen(line); i++) {
    if(line[i] == ' ' || line[i] == ';')
      return 0;
    if(line[i] == ':')
      return 1;
  }
  return false;
  return 0;
}

bool is_opcode(char *line) {
int
isopcode(char* line)
{
  int i;
  char opcode[4];
  substr(line, opcode, 0, 3);
  ucstr(opcode);
  return opcode_exists(opcode);
  suca(sstr(line, opcode, 0, 3));
  for(i = 0; i < 56; i++)
    if(scmp(OPCODES[i], opcode))
      return 1;
  return 0;
}

bool is_directive(char *line) { return line[0] == '.'; }
int
iscomment(char* line)
{
  return line[0] == ';';
}

bool is_spacer_comment(char *line) { return line[0] == ';' && line[1] == ';'; }
int
isdirective(char* line)
{
  return line[0] == '.';
}

bool is_inline_comment(char *line) { return line[0] == ';' && line[1] == ' '; }
int
isvariable(char* line)
{
  return spos(line, ".dsb") > 0;
}

bool is_variable(char *line) {
  int i, len = strlen(line);
  for (i = 0; i < len; i++) {
    if (line[i] == '.' && line[i + 1] == 'd' && line[i + 2] == 's' &&
        line[i + 3] == 'b') {
      return true;
    }
  }
  return false;
int
isconstant(char* line)
{
  return spos(line, ".equ") > 0;
}

bool is_constant(char *line) {
  int i, len = strlen(line);
  for (i = 0; i < len; i++) {
    if (line[i] == '.' && line[i + 1] == 'e' && line[i + 2] == 'q' &&
        line[i + 3] == 'u') {
      return true;
    }
  }
  return false;
void
dolabel(char* src)
{
  char key[BUFLEN], cmt[BUFLEN];
  setkey(src, key);
  setcmt(src, cmt);
  key[0] = cuca(key[0]);
  if(key[0] == '@' || skipped)
    printf("%s %s\n", key, cmt);
  else
    printf("\n%s %s\n", key, cmt);
  skipped = 0;
}

void save(char *filepath, char *output, bool do_inplace) {
  if (do_inplace) {
    FILE *fw = fopen(filepath, "w");
    fprintf(fw, "%s", output);
    fclose(fw);
void
doopcode(char* src)
{
  char key[BUFLEN], val[BUFLEN], cmt[BUFLEN];
  setkey(src, key);
  suca(key);
  setval(src, val);
  setcmt(src, cmt);
  if(slen(cmt) > 1)
    printf("\t%s %-30s %s\n", key, val, cmt);
  else
    printf("\t%s %s\n", key, val);
  skipped = 0;
}

void
docomment(char* src)
{
  if(src[0] == src[1]) {
    printf("\n%s\n\n", src);
    skipped = 1;
  } else {
    printf("%s\n", output);
    printf("\t%s\n", src);
    skipped = 0;
  }
}

void lint(char *filepath, char *output, bool do_debug) {
  FILE *fr = fopen(filepath, "r");
  int id = 0, len, count = 0, opcodes = 0, directives = 0, spacers = 0,
      labels = 0, variables = 0, constants = 0, comments = 0;
  bool skipped_last = false;
  char line[BUFLEN];
  char trimed[BUFLEN];
  while (fgets(line, BUFLEN, fr)) {
    id++;
    trimstr(line, trimed);
    swapcstr(trimed, '\t', ' ');
    len = strlen(trimed);
    if (len < 3) {
void
dodirective(char* src)
{
  char key[BUFLEN], val[BUFLEN], cmt[BUFLEN];
  setkey(src, key);
  slca(key);
  setval(src, val);
  setcmt(src, cmt);
  if(slen(cmt) > 1)
    printf("\t%s %s %s\n", key, val, cmt);
  else
    printf("\t%s %s\n", key, val);
  skipped = 0;
}

void
dovariable(char* src)
{
  char key[BUFLEN], val[BUFLEN], cmt[BUFLEN];
  setkey(src, key);
  slca(key);
  setval(src, val);
  setcmt(src, cmt);
  if(slen(cmt) > 1)
    printf("\t%-23s %-10s %s\n", key, val, cmt);
  else
    printf("\t%-23s %s\n", key, val);
  skipped = 0;
}

void
doconstant(char* src)
{
  char key[BUFLEN], val[BUFLEN], cmt[BUFLEN];
  setkey(src, key);
  suca(key);
  setval(src, val);
  setcmt(src, cmt);
  printf("\t%-23s %-10s %s\n", key, val, cmt);
  skipped = 0;
}

void
lint(FILE* f)
{
  char line[BUFLEN], trimed[BUFLEN];
  while(fgets(line, BUFLEN, f)) {
    scpy(strm(line), trimed);
    if(slen(trimed) < 3)
      continue;
    }
    if (is_label(trimed)) {
      char key[BUFLEN];
      char comment[BUFLEN];
      set_line_key(trimed, key);
      set_line_comment(trimed, comment);
      if (!is_capital(key)) {
        printf("Lin6: Label %s not capitalized, at %s:%d.\n", key, filepath,
               id);
      }
      if (key[0] != '@' && !skipped_last) {
        strcat(output, "\n");
      }
      if (has_comment(line)) {
        strcat(output, key);
        strcat(output, " ");
        strcat(output, comment);
      } else {
        strcat(output, key);
      }
      skipped_last = false;
      labels++;
    } else if (is_opcode(trimed)) {
      char key[BUFLEN];
      char value[BUFLEN];
      char comment[BUFLEN];
      set_line_key(trimed, key);
      set_line_value(trimed, value);
      set_line_comment(trimed, comment);
      ucstr(key);
      strcat(output, "\t");
      strcat(output, key);
      strcat(output, " ");
      if (has_comment(line)) {
        fillstr(value, 24, ' ');
        strcat(output, value);
        strcat(output, " ");
        strcat(output, comment);
      } else {
        strcat(output, value);
      }
      skipped_last = false;
      opcodes++;
    } else if (is_directive(trimed)) {
      char key[BUFLEN];
      char value[BUFLEN];
      char comment[BUFLEN];
      set_line_key(trimed, key);
      set_line_value(trimed, value);
      set_line_comment(trimed, comment);
      strcat(output, "\t");
      strcat(output, key);
      strcat(output, " ");
      if (has_comment(line)) {
        strcat(output, value);
        strcat(output, " ");
        strcat(output, comment);
      } else {
        strcat(output, value);
      }
      skipped_last = false;
      directives++;
    } else if (is_inline_comment(trimed)) {
      strcat(output, "\t");
      strcat(output, trimed);
      skipped_last = false;
      comments++;
    } else if (is_spacer_comment(trimed)) {
      strcat(output, "\n");
      strcat(output, trimed);
      strcat(output, "\n");
      skipped_last = true;
      spacers++;
    } else if (is_variable(trimed)) {
      char key[BUFLEN];
      char value[BUFLEN];
      char comment[BUFLEN];
      set_line_key(trimed, key);
      set_line_value(trimed, value);
      set_line_comment(trimed, comment);
      fillstr(key, 24, ' ');
      if (!is_lower(key)) {
        printf("Lin6: Variable %s not lowercase, at %s:%d.\n", key, filepath,
               id);
      }
      strcat(output, key);
      strcat(output, value);
      strcat(output, " ");
      strcat(output, comment);
      skipped_last = false;
      variables++;
    } else if (is_constant(trimed)) {
      char key[BUFLEN];
      char value[BUFLEN];
      char comment[BUFLEN];
      set_line_key(trimed, key);
      set_line_value(trimed, value);
      set_line_comment(trimed, comment);
      fillstr(key, 20, ' ');
      if (!is_upper(key)) {
        printf("Lin6: Constant %s not uppercase, at %s:%d.\n", key, filepath,
               id);
      }
      strcat(output, key);
      strcat(output, value);
      strcat(output, " ");
      strcat(output, comment);
      skipped_last = false;
      constants++;
    } else {
      printf("Lin6: Unknown line-type, at %s:%d.\n", filepath, id);
      strcat(output, line);
    }
    strcat(output, "\n");
    count++;
  }
  if (do_debug) {
    printf("Lin6 debug: %d lines: \n", count);
    printf("\t%d labels\n", labels);
    printf("\t%d opcodes\n", opcodes);
    printf("\t%d directives\n", directives);
    printf("\t%d spacers\n", spacers);
    printf("\t%d variables\n", variables);
    printf("\t%d constants\n", constants);
    if(islabel(trimed))
      dolabel(trimed);
    else if(isopcode(trimed))
      doopcode(trimed);
    else if(iscomment(trimed))
      docomment(trimed);
    else if(isdirective(trimed))
      dodirective(trimed);
    else if(isvariable(trimed))
      dovariable(trimed);
    else if(isconstant(trimed))
      doconstant(trimed);
    else
      printf("%s\n", line);
  }
  fclose(fr);
  fclose(f);
}

void show_version(void) { puts("Lin6 " VERSION); }
int
version(void)
{
  puts("Lin6 " VERSION);
  return 0;
}

void show_help(void) {
  show_version();
int
help(void)
{
  version();
  puts("");
  puts("A tool to format 6502 assembly code.\n");
  puts("Usage:  Lin6 [-options] [<file> ...]\n");
  puts("\t-?\tShow this help");
  puts("\t-v\tShow version");
  puts("\t-i\tInplace edit files");
  puts("\t-d\tShow debug\n");
  puts("See README for more info.\n");
  return 0;
}

int main(int argc, char *argv[]) {
  bool do_debug = false, do_inplace = false;
int
main(int argc, char* argv[])
{
  FILE* f;
  int i;
  if (argc < 2) {
    show_help();
    exit(0);
  }
  for (i = 1; i < argc; i++) {
    if (argv[i][1] == '?') {
      show_help();
      exit(0);
    } else if (argv[i][1] == 'v') {
      show_version();
      exit(0);
    }
    /* read flags */
    if (argv[i][1] == 'd') {
      do_debug = true;
    } else if (argv[i][1] == 'i') {
      do_inplace = true;
    }
    /* do files */
  if(argc < 2)
    return help();
  for(i = 1; i < argc; i++) {
    if(argv[i][1] == '?')
      return help();
    else if(argv[i][1] == 'v')
      return version();
    else {
      char linted[51200];
      if (!file_exists(argv[i])) {
        printf("Lin6: File %s not found\n", argv[i]);
        exit(0);
      }
      lint(argv[i], linted, do_debug);
      save(argv[i], linted, do_inplace);
      f = fopen(argv[i], "r");
      if(f != NULL)
        lint(f);
    }
  }
  return 0;
}
\ No newline at end of file
}