aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/casm.html9
-rw-r--r--src/6502.c10
-rw-r--r--src/casm.c4
-rw-r--r--src/example/Makefile5
-rw-r--r--src/example/gb.asm4
-rw-r--r--src/example/vcs.asm533
-rw-r--r--src/gbcpu.c20
-rw-r--r--src/gbout.c7
-rw-r--r--src/listing.c2
9 files changed, 586 insertions, 8 deletions
diff --git a/doc/casm.html b/doc/casm.html
index 138db3c..245a1ee 100644
--- a/doc/casm.html
+++ b/doc/casm.html
@@ -388,6 +388,13 @@ The following are built-in aliases for the above directives.
<thead><tr><td class="head">Command</td>
<td class="head">Built-in Alias</td></tr></thead>
+<tr><td class="cmd">processor</td>
+<td class="alias">
+proc<br>
+arch<br>
+cpu<br>
+</td></tr>
+
<tr><td class="cmd">equ</td>
<td class="alias">
eq
@@ -1253,7 +1260,7 @@ option zero-page, &lt;on|off|auto&gt;
</td>
<td class="def">
Controls the assumptions made regarding Zero Page address. Defaults to
-<i>off</i>, and can be the following values:
+<i>auto</i>, and can be the following values:
<table>
<tr><td class="cmd">
diff --git a/src/6502.c b/src/6502.c
index ddaf482..a6b377e 100644
--- a/src/6502.c
+++ b/src/6502.c
@@ -415,6 +415,7 @@ static CommandStatus ADC(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0x79);
PCWriteWord(address);
return CMD_OK;
@@ -472,6 +473,7 @@ static CommandStatus AND(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0x39);
PCWriteWord(address);
return CMD_OK;
@@ -598,6 +600,7 @@ static CommandStatus CMP(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0xd9);
PCWriteWord(address);
return CMD_OK;
@@ -756,6 +759,7 @@ static CommandStatus EOR(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0x59);
PCWriteWord(address);
return CMD_OK;
@@ -899,6 +903,7 @@ static CommandStatus LDA(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0xb9);
PCWriteWord(address);
return CMD_OK;
@@ -1082,6 +1087,7 @@ static CommandStatus ORA(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0x19);
PCWriteWord(address);
return CMD_OK;
@@ -1223,6 +1229,7 @@ static CommandStatus SBC(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0xf9);
PCWriteWord(address);
return CMD_OK;
@@ -1275,6 +1282,7 @@ static CommandStatus STA(const char *label, int argc, char *argv[],
return CMD_OK;
case ABSOLUTE_INDEX_Y:
+ case ZERO_PAGE_INDEX_Y:
PCWrite(0x99);
PCWriteWord(address);
return CMD_OK;
@@ -1484,7 +1492,7 @@ static const HandlerTable handler_table[] =
void Init_6502(void)
{
- option.zp_mode = ZP_OFF;
+ option.zp_mode = ZP_AUTO;
}
diff --git a/src/casm.c b/src/casm.c
index 0553573..3213bb0 100644
--- a/src/casm.c
+++ b/src/casm.c
@@ -502,6 +502,10 @@ static struct
{".cpu", ARCH},
{"arch", ARCH},
{".arch", ARCH},
+ {"proc", ARCH},
+ {".proc", ARCH},
+ {"processor", ARCH},
+ {".processor", ARCH},
{"option", OPTION},
{".option", OPTION},
{"opt", OPTION},
diff --git a/src/example/Makefile b/src/example/Makefile
index d184de0..906bb44 100644
--- a/src/example/Makefile
+++ b/src/example/Makefile
@@ -20,7 +20,7 @@
# Makefile for examples
#
-ALL = spectrum.tap c64.t64 zx81.p gb.rom
+ALL = spectrum.tap c64.t64 zx81.p gb.rom vcs.bin
CASM = ../casm
all: $(ALL) $(CASM)
@@ -42,5 +42,8 @@ zx81.p: zx81.asm $(CASM)
gb.rom: gb.asm $(CASM)
$(CASM) gb.asm
+vcs.bin: vcs.asm $(CASM)
+ $(CASM) vcs.asm
+
clean:
rm -f $(ALL)
diff --git a/src/example/gb.asm b/src/example/gb.asm
index 15cc50b..473ac89 100644
--- a/src/example/gb.asm
+++ b/src/example/gb.asm
@@ -1,9 +1,7 @@
cpu gameboy
- option +list
- option list-labels,all
- option output-file,gb.rom
+ option output-file,gb.gb
option output-format,gameboy
option gameboy-irq,vbl,vbl_code
diff --git a/src/example/vcs.asm b/src/example/vcs.asm
new file mode 100644
index 0000000..379722c
--- /dev/null
+++ b/src/example/vcs.asm
@@ -0,0 +1,533 @@
+;---------------------------------------------------------------------------
+
+; * Subject: [stella] 2600 Digital Clock (source code)
+; * From: crackers@hwcn.org
+; * Date: Sun, 5 Oct 1997 14:31:50 -0400 (EDT)
+
+;---------------------------------------------------------------------------
+
+; Here's the source code for that final digital clock programme.
+; Feel free to employ and distribute this code however you may wish.
+; Both the source code and the binary are public domain.
+
+; -----------------------------------------------------------------------------
+ processor 6502
+
+ option output-file,vcs.bin
+
+ option +list
+ option +list-hex
+ option +list-pc
+
+VSYNC equ $00
+VBLANK equ $01
+WSYNC equ $02
+NUSIZ0 equ $04
+NUSIZ1 equ $05
+COLUPF equ $08
+COLUBK equ $09
+PF0 equ $0D
+PF1 equ $0E
+PF2 equ $0F
+SWCHA equ $280
+INTIM equ $284
+TIM64T equ $296
+CTRLPF equ $0A
+COLUP0 equ $06
+COLUP1 equ $07
+GP0 equ $1B
+GP1 equ $1C
+HMOVE equ $2a
+RESP0 equ $10
+RESP1 equ $11
+
+;RAM
+
+TEMP equ $80 ;2 bytes for temporary data
+SECS equ $82 ;seconds counter
+MINS equ $83 ;minutes counter
+HOURS equ $84 ;hours counter
+JOYDEL equ $85 ;joystick delay variable
+JOY1ST equ $86 ;joystick first move variable
+SPRITEA equ $87 ;8 bytes for the first sprite
+SPRITEB equ $8F ;8 bytes for the second sprite
+RMINS equ $97 ;real minutes
+RHOURS equ $98 ;real hours
+FRAMES equ $99 ;frames counter
+
+ org $F000
+
+start SEI
+ CLD
+ LDX #$FF
+ TXS
+ LDA #$00
+
+zero STA $00,X ;looks familiar, right?
+ DEX ;typical zeroing routine
+ BNE zero
+
+ lda #$01 ;now we set up all our variables
+ sta CTRLPF
+ lda #$0C ;set our starting time at 12:00
+ sta HOURS ;just like a VCR, eh? Except it doesn't blink
+ lda #$3C ;00 minutes
+ sta MINS
+ lda #$ca ;nice pretty green for our sprites
+ sta COLUP0
+ sta COLUP1
+ lda #$07 ;make them good and fat
+ sta NUSIZ0
+ sta NUSIZ1
+ lda #$3C ;initialize the frame and seconds counters
+ sta FRAMES
+ sta SECS
+
+main JSR vertb ;main loop
+ JSR time
+ JSR draw
+ JSR clear
+ JMP main
+
+vertb LDX #$00 ;vertical blank, We all know what this is about
+ LDA #$02
+ STA WSYNC
+ STA WSYNC
+ STA WSYNC
+ STA VSYNC
+ STA WSYNC
+ STA WSYNC
+ LDA #$2C
+ STA TIM64T
+ LDA #$00
+ STA WSYNC
+ STA VSYNC
+ RTS
+
+time ldy #06 ;just load Y ahead of time for #of sprite lines
+ lda #$3C ;60
+ sec
+ sbc MINS ;subtract the clock minutes from 60 to get the
+ sta RMINS ;real minutes since clock counts down
+ cmp #$00 ;see if it's 00 minutes
+ beq min0
+ cmp #$32 ;see if it's more than 50 minutes
+ bpl min5
+ cmp #$28 ;see if it's more than 40 minutes
+ bpl min4
+ cmp #$1E ;see if it's more than 30 minutes
+ bpl min3
+ cmp #$14 ;see if it's more than 20 minutes
+ bpl min2
+ cmp #$0A ;see if it's more than 10 minutes
+ bpl min1
+
+min0 lda zeros,y ;minutes must be less than 10 so load 00 sprite
+ and #$F0 ;strip the first 4 bits
+ sta SPRITEA,y ;store it to sprite A memory
+ dey
+ bpl min0 ;get next sprite line
+ lda #$00 ;less than 10 minutes
+ jmp minload ;go to where we load the first 4 bits of sprite
+
+min5 lda fives,y ;minutes must be 50+ so load 55 sprite
+ and #$F0 ;strip 1st four bits
+ sta SPRITEA,y ;store it to sprite A memory
+ dey
+ bpl min5 ;get next sprite line
+ lda #$32 ;50+ minutes - you'll need this number later to
+ jmp minload ;load the second half the sprite data
+
+min4 lda fours,y ;minutes must be 40+
+ and #$F0
+ sta SPRITEA,y
+ dey
+ bpl min4
+ lda #$28 ;40+ minutes
+ jmp minload
+
+min3 lda threes,y ;minutes must be 30+
+ and #$F0
+ sta SPRITEA,y
+ dey
+ bpl min3
+ lda #$1E ;30+ minutes
+ jmp minload
+
+min2 lda twos,y ;minutes must be 20+
+ and #$F0
+ sta SPRITEA,y
+ dey
+ bpl min2
+ lda #$14
+ jmp minload ;20+ minutes
+
+min1 lda ones,y ;minutes must be 10+
+ and #$F0
+ sta SPRITEA,y
+ dey
+ bpl min1
+ lda #$0A ;10+ minutes
+
+minload STA TEMP ;the accumulator had the 10s of minutes
+ LDA RMINS ;now we subtract the 10s minutes from the real
+ sec ;minutes to get the 1s minutes to act as a pointer
+ SBC TEMP ;for the data tables for 2nd half of sprite
+ ASL ;double the number
+ TAX
+ LDA numblk,x ;load the first half of the sprite data address
+ sta TEMP
+ lda numblk+1,x ;load the second half of the sprite table address
+ sta TEMP+1
+
+ ldy #$06 ;number of lines in the sprite (-1)
+msload lda (TEMP),y ;get the sprite data
+ and #$0F ;strip off the last 4 bits
+ ora SPRITEA,y ;combine the 1st half with the 2nd half
+ sta SPRITEA,y ;put it back in the sprite memory
+ dey
+ bpl msload ;get the next line of data
+
+ ldy #$06 ;preload number of sprite lines (-1)
+ lda #$18 ;24 hours
+ sec
+ SBC HOURS ;subtract the counter hours to get
+ STA RHOURS ;the real hours value
+ cmp #$00 ;see if it's 12:00 am
+ beq hour0
+ cmp #$14 ;see if it's 20+ hours
+ bpl hour2
+ cmp #$0A ;see if it's 10+ hours
+ bpl hour1
+
+hour0 lda zeros,y ;load the zeros sprite data
+ and #$F0 ;strip the 1st four bits
+ sta SPRITEB,y ;store to the 2nd sprite memory
+ dey
+ bpl hour0
+ lda #$00 ;same deal as with the minutes
+ jmp loadhrs ;but now we load the second half of the hours data
+
+hour1 lda ones,y
+ and #$F0
+ sta SPRITEB,y
+ dey
+ bpl hour1
+ lda #$0A
+ jmp loadhrs
+
+hour2 lda twos,y
+ and #$F0
+ sta SPRITEB,y
+ dey
+ bpl hour2
+ lda #$14
+ jmp loadhrs
+
+loadhrs STA TEMP
+ LDA RHOURS
+ sec
+ SBC TEMP
+ asl
+ tax
+ lda numblk,x
+ sta TEMP
+ lda numblk+1,x
+ sta TEMP+1
+
+ ldy #$06
+hsload lda (TEMP),y
+ and #$0F
+ ora SPRITEB,y
+ sta SPRITEB,y
+ dey
+ bpl hsload
+ rts
+
+numblk .word zeros ;where all the sprites are at
+ .word ones
+ .word twos
+ .word threes
+ .word fours
+ .word fives
+ .word sixes
+ .word sevens
+ .word eights
+ .word nines
+
+draw LDA INTIM ;check to see if it's time to draw a frame
+ BNE draw
+ sta WSYNC
+ sta HMOVE
+ sta VBLANK ;turn the screen on!
+
+;insert display kernal
+
+ ldx #$3F ;okay, this display kernal sucks, but I'm not doing
+blow1 sta WSYNC ;much here so I didn't go for anything fancy since
+ dex ;this is just a demo. This wouldn't be the way you
+ bpl blow1 ;do things in a game, but it works for this.
+ sta WSYNC
+ nop ;See... you'd never do something weenie like this
+ nop ;in a real programme
+ nop ;
+ nop ;
+ nop ;but when I was experimenting with this programme
+ nop ;I just had a whole bunch of ";nop" lines here
+ nop ;and I removed the ";" until I got the spacing more
+ nop ;or less where I wanted it
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ sta RESP0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ sta RESP1
+
+ ldy #$06
+sload lda SPRITEB,y
+ sta GP0
+ lda SPRITEA,y
+ sta GP1
+ sta WSYNC ;you wouldn't do something weenie like this
+ sta WSYNC ;either in a real programme, but it was an
+ sta WSYNC ;easy way to make each sprite 8 lines high
+ sta WSYNC ;and I was more concerned with making a working
+ sta WSYNC ;and accurate clock than a nice display.
+ sta WSYNC
+ sta WSYNC
+ sta WSYNC
+ dey
+ bpl sload
+ lda #$00
+ sta GP0
+ sta GP1
+
+ ldx #$48
+blow2 sta WSYNC ;now we just blow the rest of the unused scanlines.
+ dex
+ bpl blow2
+ rts
+
+
+clear LDA #$24 ;set timer for overscan
+ STA TIM64T
+ LDA #$02 ;clear the screen and turn off the video
+ STA WSYNC
+ STA VBLANK
+ LDA #$00
+ STA PF0
+ STA PF1
+ STA PF2
+ sta COLUPF
+ sta COLUBK
+
+ LDA #$3C ;this is the clock routine itself. it counts
+ DEC FRAMES ;down from 60 frames, and then decreases the
+ bne joy ;seconds, which count down the minutes and then
+ lda #$3C ;the hours.. etc. For whatever reason my 2600
+ STA FRAMES ;wasn't running at exactly 60 frames a second
+ DEC SECS ;so there were two lines inserted to correct
+ bne joy ;timing accuracy problems
+ STA SECS
+ DEC SECS ;here's one. Kept me from losing a second every
+ DEC MINS ;minute
+ bne joy
+ STA MINS
+ LDA #$18
+ INC SECS ;here's the other. It kept me from gaining a
+ DEC HOURS ;second every hour.
+ bne joy
+ STA HOURS
+ ;now my timing inaccuracies may have been caused
+ ;by either my V-blank, V-sync, Overscan, or
+ ;display being a few scanlines too long or short.
+ ;theoretically if all my lines were bang on,
+ ;I wouldn't have needed those two seconds counter
+ ;corrections. But with them inplace, it allows me
+ ;to be a little looser with my code which works for
+ ;me. It may still gain or lose a second every 60
+ ;hours, but I can live with that. And since I'll
+ ;be employing this clock in a virtual pet game and
+ ;not a swiss made olympic time piece, a little
+ ;inaccuracy won't matter.
+
+joy lda SWCHA ;load joysticks
+ ora #$0f ;strip the data for player #2 joystick
+ cmp #$ef ;up
+ beq up
+ cmp #$df ;down
+ beq down
+ cmp #$bf ;left
+ beq left
+ cmp #$7f ;right
+ beq right
+ lda #$00 ;no movement
+ sta JOYDEL ;reset the joystick delay variable
+ lda #$01 ;reset the first move variable
+ sta JOY1ST
+ jmp oscan ;finish off the overscan
+
+up lda HOURS ;check to see if we've run out our hours
+ cmp #$01
+ beq oscan ;yep, then ignore the movement
+ inc JOYDEL ;increase the joystick delay variable
+ lda JOY1ST ;check to see if this is the first move in this
+ cmp #$01 ;direction.
+ beq now1 ;if it is then change the variable now
+ lda #$1E ;nope then see if there's been enough of a delay
+ cmp JOYDEL ;to change the variable yet.
+ bne oscan
+now1 lda #$00 ;reset the joystick delay and set the first move
+ sta JOY1ST ;indicator to "no"
+ sta JOYDEL
+ dec HOURS ;decrease the hours counter
+ jmp oscan
+
+down lda HOURS
+ cmp #$18
+ beq oscan
+ inc JOYDEL
+ lda JOY1ST
+ cmp #$01
+ beq now2
+ lda JOYDEL
+ cmp #$1E
+ bne oscan
+now2 lda #$00
+ sta JOY1ST
+ sta JOYDEL
+ inc HOURS ;increase the hours counter
+ jmp oscan
+
+left lda MINS
+ cmp #$01
+ beq oscan
+ inc JOYDEL
+ lda JOY1ST
+ cmp #$01
+ beq now3
+ lda #$1E
+ cmp JOYDEL
+ bne oscan
+now3 lda #$00
+ sta JOY1ST
+ sta JOYDEL
+ dec MINS ;decrease the minutes counter
+ jmp oscan
+
+right lda MINS
+ cmp #$3c
+ beq oscan
+ inc JOYDEL
+ lda JOY1ST
+ cmp #$01
+ beq now4
+ lda #$1E
+ cmp JOYDEL
+ bne oscan
+now4 lda #$00
+ sta JOY1ST
+ sta JOYDEL
+ inc MINS ;increase the minutes counter
+
+oscan lda INTIM ;see if the timer has run out
+ BNE oscan
+ STA WSYNC
+ RTS
+
+zeros .byte 11100111b ;sprites are stored upsidedown, and there
+ .byte 10100101b ;are two copies of each number in each sprite
+ .byte 10100101b ;location. The unwanted number is stripped
+ .byte 10100101b ;with the AND command (AND #$0F for the right
+ .byte 10100101b ;number stripped, AND #F0 for the left)
+ .byte 10100101b ;then any two numbers can be combined with an
+ .byte 11100111b ;OR command. Neat huh?
+
+ones .byte 11100111b
+ .byte 01000010b
+ .byte 01000010b
+ .byte 01000010b
+ .byte 01000010b
+ .byte 11000110b
+ .byte 01000010b
+
+twos .byte 11100111b
+ .byte 10000100b
+ .byte 10000100b
+ .byte 11100111b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+
+threes .byte 11100111b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+
+fours .byte 00100001b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+ .byte 10100101b
+ .byte 10100101b
+ .byte 10000100b
+
+fives .byte 11100111b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+ .byte 10000100b
+ .byte 10000100b
+ .byte 11100111b
+
+sixes .byte 11100111b
+ .byte 10100101b
+ .byte 10100101b
+ .byte 11100111b
+ .byte 10000100b
+ .byte 10000100b
+ .byte 11000110b
+
+sevens .byte 10000100b
+ .byte 10000100b
+ .byte 10000100b
+ .byte 01000010b
+ .byte 00100001b
+ .byte 00100001b
+ .byte 11100111b
+
+eights .byte 11100111b ;This code is (c)1997 by Chris "Crackers" Cracknell
+ .byte 10100101b ;and is placed in the Public Domain by the author.
+ .byte 10100101b ;Anyone is free to employ and distribute this code
+ .byte 11100111b ;as they see fit.
+ .byte 10100101b ;
+ .byte 10100101b ;
+ .byte 11100111b ;
+ ;
+nines .byte 00100001b ;Well... if you're going to use this code in a
+ .byte 00100001b ;"Doomsday Machine" to destroy the world, then
+ .byte 00100001b ;I would rather you didn't. But otherwise, knock
+ .byte 11100111b ;yourself out with it.
+ .byte 10100101b ;
+ .byte 10100101b ;Actually... if the "Doomsday Machine" is just in
+ .byte 11100111b ;a game, then it's okay to use the code.
+ ;
+ org $FFFC ;Unless it's like the movie "War Games" where the
+ .word start ;computer running the game is hooked up to a real
+ .word start ;"Doomsday Machine" then it wouldn't be a good idea.
+
+
+
diff --git a/src/gbcpu.c b/src/gbcpu.c
index a4fffe5..9f33bf5 100644
--- a/src/gbcpu.c
+++ b/src/gbcpu.c
@@ -819,6 +819,23 @@ static CommandStatus LD(const char *label, int argc, char *argv[],
return CMD_OK;
}
+ /* LD ($ff00 + n), A
+ */
+ if (r1 == A8 && r2 == ADDRESS && off2 >= 0xff00)
+ {
+ PCWrite(0xf0);
+ PCWrite(off2 - 0xff00);
+ return CMD_OK;
+ }
+
+ /* LD ($ff00 + n), A
+ */
+ if (r1 == ADDRESS && r2 == A8 && off1 >= 0xff00)
+ {
+ PCWrite(0xe0);
+ PCWrite(off1 - 0xff00);
+ return CMD_OK;
+ }
/* Custom opcode generation using the codes table
*/
@@ -1019,7 +1036,8 @@ static CommandStatus ADD(const char *label, int argc, char *argv[],
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xc6, WRITE_BYTE_RHS},
- A8, HL_ADDRESS, {0x86}
+ A8, HL_ADDRESS, {0x86},
+ SP16, VALUE, {0xe8, WRITE_WORD_RHS}
};
RegisterMode r1, r2;
diff --git a/src/gbout.c b/src/gbout.c
index 8fa3e7a..9b73655 100644
--- a/src/gbout.c
+++ b/src/gbout.c
@@ -261,6 +261,13 @@ int GBOutput(const char *filename, const char *filename_bank,
mem = bank[0]->memory;
+ /* Create the log
+ */
+ for(f = 0; logo[f] != -1; f++)
+ {
+ PokeB(mem, 0x104 + f, logo[f]);
+ }
+
/* Create the RST handlers
*/
for(f = 0; f < 8; f++)
diff --git a/src/listing.c b/src/listing.c
index 2168309..59c3491 100644
--- a/src/listing.c
+++ b/src/listing.c
@@ -162,7 +162,7 @@ static void BuildArgs(Varchar *str, int argc, char *argv[], int quoted[])
static void Output(const char *fmt, ...)
{
- if (IsFirstPass() && options.enabled)
+ if (IsFinalPass() && options.enabled)
{
va_list va;