From a8131ea5ed00c11517c2cb605834eb103ecac250 Mon Sep 17 00:00:00 2001 From: Ian C Date: Sun, 17 Apr 2016 22:47:10 +0100 Subject: Various fixes * Added VCS example, which highlighted some bug in the zero page detection. * 6502 fixes for the above. Still a problem with code generation. * Updates to Gameboy code (example still not working in emulator). * Fixed listing not being generated when enabled. --- doc/casm.html | 9 +- src/6502.c | 10 +- src/casm.c | 4 + src/example/Makefile | 5 +- src/example/gb.asm | 4 +- src/example/vcs.asm | 533 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/gbcpu.c | 20 +- src/gbout.c | 7 + src/listing.c | 2 +- 9 files changed, 586 insertions(+), 8 deletions(-) create mode 100644 src/example/vcs.asm 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. Command Built-in Alias +processor + +proc
+arch
+cpu
+ + equ eq @@ -1253,7 +1260,7 @@ option zero-page, <on|off|auto> Controls the assumptions made regarding Zero Page address. Defaults to -off, and can be the following values: +auto, and can be the following values:
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; -- cgit v1.2.3