Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- include "hardware.inc"
- include "header.inc"
- section "queue variables", WRAM0
- QUEUE: ds 256 ;make the queue 256 large so it wraps itself using overflow
- rHEAD: ds 1
- rTAIL: ds 1
- rSIZE: ds 1 ;amount of data in the queue (the head will keep filling up the queue even if it's full so I need to keep track of the size)
- section "screen variables", WRAM0
- rSCREEN_X: ds 1
- rCURRENT_LINE_ADDR: ds 2 ;needs to be 2 bytes because I'm gonna store an address
- section "serial interrupt", ROM0[$0058]
- jp serial_interrupt
- section "vblank interrupt", ROM0[$0040]
- jp vblank_interrupt
- section "main", ROM0[$0150] ;NEED TO PUT THIS IN THE RIGHT SPOT (after header) otherwise it'll try to put it between the interrupts and the header
- main:
- di
- call init_display
- call init_serial
- ld a, 0
- ld [rIF], a ;CLEAR INTERRUPT FLAGS!!
- ei
- .loop
- halt ;everything's going to happen in vblank and serial interrupts
- jr .loop
- init_serial:
- push af
- push hl
- ld a, %10000000 ;external serial clock, normal speed, SET THE BEGIN TRANSFER BIT SO I CAN RECIEVE DATA (begin transfer bit really just means "when the serial clock ticks, latch/send data")
- ld [rSC], a
- ld a, 0
- ld [rHEAD], a
- ld [rTAIL], a
- ld [rSIZE], a ;clear head, tail, and size
- ld hl, rIE
- set 3, [hl] ;enable serial interrupt
- pop hl
- pop af
- ret
- init_display:
- push af
- push hl
- push bc
- push de
- .wait_v_blank
- ld a, [rLY]
- cp 144 ; Check if the LCD is past VBlank
- jr c, .wait_v_blank
- ld hl, $9000 ;load font data into vram
- ld de, font_tiles ;start of font data
- ld bc, font_tiles_end - font_tiles ;amount of font data (could have gone without this but then it would be a little more annoying to tell when count was 0)
- .copy_font
- ld a, [de] ; nab a byte of the font
- ld [hli], a ; put it in vram, incrementing hl
- inc de ; Move to next byte
- dec bc ; Decrement remaining byte count
- ld a, b ; Check if count is 0
- or c
- jr nz, .copy_font
- ld a, %11100100
- ld [rBGP], a ;set pallette
- ld a, 0
- ld [rSCY], a
- ld [rSCX], a ;scroll window x and y to 0
- ld hl, rIE
- set 0, [hl] ;enable vblank interrupt
- ld a, %10000001
- ld [rLCDC], a ;turn lcd back on, put it in the proper mode
- pop de
- pop bc
- pop hl
- pop af
- ret
- serial_interrupt:
- push af
- push bc
- push de
- push hl
- ld a, [rSB] ;get the recieved serial data
- ld d, a ;save data for later
- ld a, [rHEAD]
- ld c, a ;save head for later
- ld hl, QUEUE
- ld b, 0 ;so I can add bc to hl and get the address to put the data into
- add hl, bc ;set hl to QUEUE[HEAD] (the location we want to put the data)
- ld [hl], d ;load data into the queue
- inc a ;increment the head
- ld [rHEAD], a
- ld hl, rSIZE
- ld a, [hl] ;I could just do ld a [rSIZE] but I want to use hl agian later so
- ld b, 255
- cp b
- jr z, .return
- inc [hl] ;increment the size of the queue if it's not 255
- .return
- ld hl, rSC
- set 7, [hl] ;BEGIN SERIAL TRANSFER AGIAN so that when the external clock ticks I recieve more data
- pop hl
- pop de
- pop bc
- pop af
- reti
- ;@return CURRENT_LINE_ADDR in hl
- get_current_line_addr:
- push af
- ld hl, rCURRENT_LINE_ADDR
- ld a, [hl]
- inc hl ;go to 2nd byte of rCURRENT_LINE_ADDRESS
- ld l, [hl] ;set low nybble of line address
- ld h, a ;set high nybble of line address
- pop af
- ret
- vblank_interrupt:
- push af
- push bc
- push de
- push hl
- ie ;SO THAT YOU CAN GET SERIALS IN THE MIDDLE OF THIS
- ld a, [rSCREEN_X] ;get screen_x for later use
- ld c, a ;store in C so I can later set B to 0 and add hl, bc
- .draw_loop
- ld a, [rSIZE] ;check how much data is in the queue
- cp 0
- jr z, .return ;stop looping if there's no data left to draw
- ld hl, QUEUE
- ld a, [rTAIL]
- ld d, 0 ;set d to 0 so I can add hl, e
- ld e, a
- add hl, de ;hl = QUEUE[rTAIL] (theres no add de, hl opcode sadly)
- ld d, [hl] ;get current byte of data
- call get_current_line_addr ;hl = CURRENT_LINE_ADDRESS
- ld b, 0
- add hl, bc ;hl = TILEMAP[CURRENT_LINE_ADDRESS + SCREEN_X]
- ld [hl], d ;finally, load the current data byte into the tilemap
- ld a, [rSTAT]
- ld b, %00000011 ;bit mask to get bottom 2 bits out of a
- and a, b
- cp 1 ;check if bottom 2 bits of lcdc STAT register are 1
- jr nz, .return ;if it's not currently vblank then don't update anything (since this means previous instructions may have been done outside of vblank)
- ld hl, rSIZE ;decrease queue size
- dec [hl] ;I DONT THINK THIS IS UNSAFE BUT YOU MIGHT WANNA MAKE SURE (can an interrupt occur in the middle of a multi cycle instruction?), ALSO KEEP IN MIND THAT THIS IS THE ONLY FUNC THAT DECREASES IT SO IT CAN'T GO BELOW 0 BY ACCIDENT
- ld hl, rTAIL
- inc [hl] ;move tail forward
- inc c ;increase the tile number
- ld a, c
- cp 20 ;check if tile number is larger than 20
- jr c, .draw_loop ;if tilenum - 20 < 0 we don't need to update the line address. otherwise, we do
- ld c, 0 ;set SCREEN_X to 0 since we're going to the beginning of the next line on the screen
- call get_current_line_addr ;hl = CURRENT_LINE_ADDR
- ld de, 32
- add hl, de ;CURRENT_LINE_ADDR += 32 (go to the next line on the screen)
- ;CHECK IF NEXT LINE IS OUT OF TILEMAP, IF IT IS THEN RESET CURRENT_LINE_ADDR TO THE START OF THE TILEMAP
- ;THINK ABOUT 256 QUEUE THING (SIZE CAN ONLY BE 255 SO CAN I REALLY JUST LET IT WRAP VIA OVERFLOW?)
- ;THINK ABOUT WHETHER I NEED TO BOTHER GETTING SCREEN_X AND KEEPING IT IN A REGISTER (and if not bothering to do that even changes anything)
- ;FIGURE OUT IF AN INTERRUPT CAN HAPPEN IN A MULTI CYCLE INSTRUCTION
- jr .draw_loop
- .return
- pop af
- pop bc
- pop de
- pop hl
- ret ;NO RETI BECAUSE NESTED INTERRUPTS
- font_tiles
- incbin "C:\\Users\\Wakydawgster\\Documents\\Programmable Memes\\Assembly SLUDGE\\Gameboy Games\\Serial recv\\font.chr"
- font_tiles_end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement