;***************************************************************; ;** **; ;***************************************************************; ;Mark De Smet ;Atari 2600 Game Server ;Main code. %bin 18 ;18 wide hex column %pagesize 5000 ;no silly headers ;name stepper %nosyms ;no symbol table in .lst nowarn uni ;no "at" warnings .186 ;produce code for the 80186 or 80188 %nolist ;include Moncalls.inc ;mon188 routines (jump table) include ebpcb.inc ;Peripheral Control Block definitions %list ;***************************************************************; ;** Hardware description **; ;***************************************************************; ;The system is controled via 2 control ports which are simple ; output ports. ;The LCD display is a 4 line by 40 character display module. ; This module actually acts like 2 2 line by 40 character modules, ; so there are 2 sets of addresses that it is accessed by. Code ; must access the correct half of the module to get the correct ; line. ;***************************************************************; ;** Software description **; ;***************************************************************; ;System is first initialized. ;Then the system executes until user input changes. A timer ; interrupts at a regular interval to check for input. If new ; input, a flag is set. If no new input, the system goes back ; into a halted state. ;***************************************************************; ;** Configuration **; ;***************************************************************; sramstart equ 0000h ;segment ;must be 0 sramsize equ 8000h ;size in bytes ;also ending address. ;last 8 bits must be zero for chipselect stacktop equ 1500h ;offset into sram segment. ;stacktop equ 4000h ;offset into sram segment. eprom_start equ 0FE00h ;(segment)start at FE000 LCD1IR equ 00h ;LCD display, upper 2 lines, instruction register LCD1DR equ 01h ; data register LCD2IR equ 02h ;LCD display, lower 2 lines, instruction register LCD2DR equ 03h ; data register USERINPUT equ 04h ;user input buffer ;Writes to this address will be ignored ;0 = button 1 "Release Cartridge"/"Screensaver" ;1 = button 2 "Back" ;2 = button 3 "Enter" ;3 = button 4 "Last Game" ;4 = button 5 "Random Game" ;5 = shaft encoder data1 ;6 = shaft encoder data2 ;7 = /cart inserted (0= cartridge inserted) CONTROL1 equ 06h ;control register 1 ;CAUTION, do not read from this address! ;(CONTROL1 register will contain random data!) ;0 - 6 = game select 1 (game select within a large ROM) ;7 = undefined CONTROL2 equ 08h ;control register 2 ;CAUTION, do not read from this address! ;(CONTROL2 register will contain random data!) ;0 = BUSSEL, bus select signal ; 0 = connects game bus to atari and disables screensaver ; 1 = connects game bus to 188 and enables screensaver ;1 = /CARTON, cartridge power control ; 1 = off ;2 - 4 = game select 2(selects what chip active on game bus) ; 0 = Cartridge socket ; 1 = SRAM ; 2 = Games 1 (A-M) ; 3 = Games 2 (M-Z) ; 4 = Extra games (demos & developmental) ;5 = AtariOn, atari power control signal ; 1 = atari on ;6 = /Backlight. Backlight control signal ; 1 = backlight off. ;7 = /Buzzer. Buzzer on/off control signal ; 1 = buzzer off resettimedef equ 15 ;default time to turn atari off for when reseting, in 0.1sec beeplen2def equ 1 ;default length of the beep for shaft encoder, in 0.1ms increments beeplen1def equ 100 ;default length of the beep for buttons, in 0.1ms increments shftdivdef equ 4 ;default number of times to ignore shaft input per times used. sleepdef equ 10 ;default inactivity time for backlight off; in 0.1 min bytesper equ 12 ;number of bytes per line in edit memory procedure NULL equ 0h ;string termination NL equ 0Ah ;new line (CR&LF) arrow equ 07Eh ;LCD arrow character ;***************************************************************; ;** Data segments **; ;***************************************************************; data segment at 0050h ;data segment at sramstart ;IVTable dd 256 dup (?) ; Interrupt Vector Table space LCDline db ? ;byte to tell which line on. newdata db ? ;flag to indicate new data ;0=no new data con1st db ? ;current state of control1 con2st db ? ;current state of control2 lastui db ? ;last data on input register. newinp db ? ;flag to indicate new input ;0=no new iinput;1=new button input;2=new shaft input shftnum db ? ;current 'position' of shaft encoder linenum db ? ;the current line in the list that is displayed ; at the top of the LCD ;0 is the top of list. shftcnt db ? ;number of rotations to ignore to divide shaft count resettime dw ? ;time to turn atari off for when reseting, in 0.1sec beeplen2 dw ? ;length of beep for shaft encoder, in 0.1ms increments beeplen1 dw ? ;length of beep for button, in 0.1ms increments shftdiv db ? ;number of times to ignore shaft input per times used. sleep dw ? ;time in 0.1 minutes of inactivity before backlight off buttondb db ? ;counter to debounce the pushbuttons randnum db ? ;'random' number from 00-FFh ;This variable is intentionally not initialized. ;It continuously has a new random number by being ; incremented 2000 times a second. This number will ; only be random if it is only read once per user ; input.(The randomness is dependant on the timing ; of the user button presses.) ininsta db ? ;flag to indicate if currently displaying instructions ; for instant play, or for gamelist lastone db ? ;last game played (for the "Last Game" feature) lasttwo db ? ;2 games ago lastthree db ? ;3 games ago lastfour db ? ;4 games ago curadr dw ? ;in memory edit procedure, like linenum, but containing ;the top lines address. thestack db 1000h dup (?) ;stack space stackptr db ? ;pointer to start of stack data ends gamebus segment at 0E000h ;game bus mapped at E0000h game db 1000h dup (?) gamebus ends instructions segment at 1000h ;instructions at 10000h names db (256*32) dup (?) ;game names table&links to instr. ;table format: ; each entry is 32 bytes. There are 256 entries ; First word is a the offset(from this seg) for the instruction ; text. All entries have a valid offset, even if no instr. ; The third byte and on is a NULL terminated string that is the ; game name. This is no more than 30 bytes. instructions ends ;powerup segment at 0FFFFh ;powerupvec: ;powerup ends ;***************************************************************; ;** Main Code **; ;***************************************************************; code segment at 0160h ;code segment at eprom_start assume cs:code, ds:data, es:data start: cli ;no interrupts yet ; setup LCS for sram ; mov dx, offset LCSST ;initialize sram CS ; mov ax, sramstart + 0031h ;1 wait states(start at 00000h) ; out dx, al ; mov dx, offset LCSSP ;initialize sram CS ; mov ax, (sramsize shr 4) + 003Ah ;ram end at sramsize(08000h)(=083Ah) ; out dx, al ; set up segment registers mov dx, data mov ds, dx ;data segment mov ss, dx ;stacksegment(same) mov es, dx ;extra segment(start same) ; set up stack mov sp, offset stackptr ; mov sp, stacktop ; setup general chip selects mov dx, P1CON ;Port Control Reg mov ax, 00FFh out dx, al ;GCS7-0 active ; GCS0 ;I/O ports. Must have 3 wait states for slow LCD interface mov dx, GCS0ST ;start mov ax, 0003h ;start at 0000h, 3 wait states. out dx, al mov dx, GCS0SP ;stop mov ax, 0048h ;stop at 0020h, CS enabled, IO cycles out dx, al ; GCS1 ;Game instructions and names ROM mov dx, GCS1ST ;start mov ax, 1001h ;start at 10000, 1 wait state out dx, al mov dx, GCS1SP ;stop mov ax, 200Ah ;stop at 1FFFFh, CS enables, mem cycles out dx, al ; GCS2 ; Game bus (like a 8k ROM) mov dx, GCS2ST ;start mov ax, 0E002h ;start at E0000, 2 wait states out dx, al mov dx, GCS2SP ;stop mov ax, 0E20Ah ;stop at E1FFFh, CS enables, mem cycles out dx, al ;setup timer 1 mov dx, T1CNT mov ax, 0 out dx, al mov dx, T1CMPA mov ax, 1000 ;trigger every 500us out dx, al mov dx, T1CON mov ax, 0E001h ;enable, no inhibit, intrpt, no retrig ; inc at clkout/4, internal, single max, cont. out dx, al out dx, al ;repeat for no inhibit out dx, al ;setup timer 2 mov dx, T2CNT ;clear current count mov ax, 0 out dx, al mov dx, T2CMPA mov ax, 60000 ;trigger every 30ms = 0.0005min out dx, al mov dx, T2CON mov ax, 0C001h ;enable, no inhibit, no interupt, continuous out dx, al out dx, al ;repeat for no inhibit out dx, al ;setup timer 0 mov dx, T0CNT ;clear current count mov ax, 0 out dx, al mov dx, T0CMPA mov ax, (sleepdef*200) out dx, al mov dx, T0CON mov ax, 0E009h ;enable, no inhibit, intrpt, CMPA, no retrig ;1110 0000 0000 1001 ; inc at tmr2 out, internal, single max, cont. out dx, al out dx, al ;repeat for no inhibit out dx, al ;setup IVT push ds xor ax, ax ;set ds to 0000h mov ds, ax mov bx, 48h ;timer 1 vector mov ax, offset tm1isr mov [bx], ax mov ax, seg tm1isr mov [bx+2], ax mov bx, 20h ;timer0 mov ax, offset tm0isr mov [bx], ax mov ax, seg tm0isr mov [bx+2], ax pop ds ;setup interrupts mov dx, IMASK mov ax, 00FEh out dx, al mov dx, TCUCON mov ax, 0000h out dx, al ; setup control ports ; control port 1 mov al, 00h ;select game 0 out CONTROL1, al mov con1st, al ; control port 2 mov al, 10101011b ;screen saver on, cartridge off, ROM A-M selected out CONTROL2, al ; atari on, backlight on, buzzer off mov con2st, al ;setup variables mov LCDline, 1 ;line 1 mov newdata, 0 ;no new data mov lastui, 0h ;no first input mov newinp, 0h ;clear flag mov shftnum, 65 mov linenum, 0 ;start at first entry mov resettime, resettimedef ;set default mov beeplen2, beeplen2def ;set default mov beeplen1, beeplen1def ;set default mov shftdiv, shftdivdef ;set default mov shftcnt, shftdivdef ;setup remaining times to ignore mov sleep, sleepdef ;set default mov buttondb, 0 ;start at no wait for debounce mov lastone, 0 ;make initial last list all zero mov lasttwo, 0 mov lastthree, 0 mov lastfour, 0 mov ininsta, 0 ;setup LCD display call initdisplay ;startup interrupts sti ;display credits/intro call startup ;start main code jmp mainm ;goto main menu routine. ;***************************************************************; ;** Main Menu procedure **; ;***************************************************************; ;implements the main menu. ;destorys: all (procedure at highest level, so get's all ownership) mainm proc main0: ;display main menu. call clrlcd ;clear screen (and goto 1,1) mov ax, seg maintxt ;display menu mov es, ax mov bx, offset maintxt mov al, linenum ;load current line number xor ah, ah shl ax, 5 ;find which line to start at. add bx, ax call disps mov ah, shftnum ;goto the pointer line sub ah, 64 mov al, 1 call gotoxy mov ax, seg mainptr ;display pointer mov es, ax mov bx, offset mainptr call disps ;wait until get new input main0a: hlt cmp newinp, 00 je main0a push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne main0d jmp main0b main0d: cmp newinp, 02h ;check if new shaft input jne main0e jmp main0c main0e: jmp main0a ;no new input to act on,wait more ;got new button input, do action. main0b: mov newinp, 0h ;clear flag. call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jnz main1r jmp main1a main1r: ;pressed enter, go on to do action. mov al, shftnum ;compute the line pointer on. sub al, 64 add al, linenum cmp al, 1 jne main1m call gamelst ;list games jmp main0 main1m: cmp al, 2 jne main1n call loadcart ;load cartridge jmp main0 main1n: cmp al, 3 jne main1o ;play memory mov al, con2st ;load status of control2 and al, 11100011b ;mask off chip selection or al, 10000110b ;make game SRAM active& turn off cart&buzzer off mov con2st, al out CONTROL2, al call ssoff ;turn off screensaver and reset jmp main0 main1o: cmp al, 4 jne main1p ;run cartridge test lastui, 10000000b ;check if cartridge inserted. jz main2a ;if cart inserted, go on. ;no cartridge inserted, warn user and check for override call clrlcd ;clear lcd& goto 1,1 mov bx, seg mainwarn mov es, bx mov bx, offset mainwarn call disps ;display warning message mov newinp, 0 ;clear any previous input mov cx, 100 ;wait for 2sec main2b: push cx mov cx, 10000 call delay ;wait for 2us * 10000 pop cx cmp newinp, 0 jne main2c loop main2b jmp main2d ;no button press, exit main2c: mov newinp, 0 ;clear input call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jz main2d ;if didn't press enter, exit main2a: mov al, con2st and al, 10100001b ;mask off rom select, turn on cart mov con2st, al out CONTROL2, al call ssoff ;turn off screensaver(if on) and reset atari main2d: jmp main0 main1p: cmp al, 5 jne main1q call editmem ;edit memory jmp main0 main1q: cmp al, 6 jne main1qq call setup ;setup jmp main0 main1qq: jmp main0 ;wait for more input main1a: test lastui, 00000010b ;check if pressed back jz main1b jmp main0a ;pressed back, do nothing main1b: test lastui, 00000001b ;check if pressed eject jz main1c call ejectct ;release cart jmp main0a ;wait for more input main1c: test lastui, 00011000b ;check if pressed instant jz main1d call instantp jmp main0 ;wait for more input main1d: jmp main0a ;don't know what input, wait again ;got new shaft input, update list. main0c: mov newinp, 0h ;clear flag call beep2 mov al, shftnum cmp al, 65 ;check if moved before first line jl main0f cmp al, 68 ;check if moved after last line jg main0h jmp main0 ;moved before first line, pointer to line 1, and decrement ; the linenum in list(unless already at beginning) main0f: mov shftnum, 65 cmp linenum, 0 je main0g dec linenum main0g: jmp main0 ;moved after last line, pointer to line 4, increment linenum(unless at end) main0h: mov shftnum, 68 cmp linenum, 2 ;last item to be at top. je main0g inc linenum jmp main0g jmp main0 ;main menu text, each line is 32 bytes long. maintxt: db ' Select internal game ', NL db ' Load cartridge ', NL db ' Play game from memory ', NL db ' Play cartridge ', NL db ' Edit game ', NL db ' Setup ', NL db ' ', NL db ' ', NL db NULL mainptr: db '--', arrow, NULL mainwarn: db ' Warning!', NL db ' No cartridge found.', NL db NL db ' (Press enter to override)', NULL mainm endp ;***************************************************************; ;** Game List procedure **; ;***************************************************************; ;lists the internal games. ;destroys: nothing gamelst proc push ax push bx push cx mov bl, linenum ;save screen postion of caller mov bh, shftnum push bx push es mov shftnum, 65 ;set pointer to first mov linenum, 0 ;set list to start at first game0: call clrlcd ;clear screen (and goto 1,1) mov al, 5 ;go to column 5 mov ah, 1 call gotoxy mov bx, instructions ;load for printing names mov es, bx mov bl, linenum ;load which name to start with xor bh, bh shl bx, 5 ;multiply by 5 ;(index into game name table) add bx, 2 ;goto game name in table entry call disps ;display game name mov cx, 3 ;print 4 names game0a: inc ah ;goto next line call gotoxy add bx, 32 ;go to next game name and bh, 1Fh ;mask off upper 3 bits because ; there is only 8k of table. call disps ;display next game name loop game0a mov ah, shftnum ;load pointer position sub ah, 64 ;adjust for y coord mov al, 1 ;first column call gotoxy mov ax, seg gamepointer ;display pointer mov es, ax mov bx, offset gamepointer call disps ;wait until get new input game1: hlt cmp newinp, 0 je game1 push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne game2 jmp game4 game2: cmp newinp, 02h ;check if new shaft input jne game3 jmp game5 game3: ;don't know what kind of input, wait for more input mov newinp, 0 ;clear flag jmp game1 game4: ;got button input, do action mov newinp, 0 ;clear flag call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jz game4a ;pressed enter, go on to display the instructions mov al, shftnum ;load game number sub al, 65 ;adjust add al, linenum ;(note no need to mask off bits, doing 8bit ; operations, extras will be truncated) call instlst jmp game0 ;wait for more input game4a: test lastui, 00000010b ;check if pressed back jz game4b jmp game8 ;pressed back, return game4b: test lastui, 00000001b ;check if pressed eject jz game4c call ejectct ;release cart jmp game1 ;wait for more input game4c: test lastui, 00011000b ;check if pressed instant jz game4d call instantp jmp game0 ;wait for more input game4d: jmp game1 ;don't know what input, wait again game5: ;got shaft input, update list mov newinp, 0 ;clear flag call beep2 cmp shftnum, 65 ;check if moved past top jl game6 cmp shftnum, 68 jg game7 ;pointer must still be on screen, redisplay jmp game0 game6: ;moved above screen top, move list up. dec linenum mov shftnum, 65 jmp game0 game7: ;moved below screen bottom, move list down. inc linenum mov shftnum, 68 jmp game0 game8: pop es ;restore and leave pop bx mov shftnum, bh mov linenum, bl pop cx pop bx pop ax ret gamepointer: db '--', arrow, NULL gamelst endp ;***************************************************************; ;** Display instructions procedure **; ;***************************************************************; ;lists the instructions for game in al ;destroys: nothing instlst proc push ax push bx push cx push es mov bl, linenum ;save screen position from caller mov bh, shftnum push bx instlstinstantbackentry: ;caution should be used when jumping here ; (ininsta must be 1) mov shftnum, 65 ;set to first line mov linenum, 0 ;start with game name. mov bx, instructions ;load for printing names&instructions mov es, bx instl0: call clrlcd ;clear lcd& goto 1,1 cmp linenum, 0 ;check if need to print game name jne instl0a mov bl, al ;load which name to start with xor bh, bh shl bx, 5 ;multiply by 5 ;(index into game name table) add bx, 2 ;goto game name in table entry call disps ;display game name mov bx, es:[bx-2] ;get address of instructions. mov cx, ax ;save ax mov al, 1 mov ah, 2 call gotoxy ;goto next line. mov ax, cx ;restore ax instl0a: call disps ;print instructions ;wait until get new input instl1: hlt cmp newinp, 0 je instl1 push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne instl2 jmp instl4 instl2: cmp newinp, 02h ;check if new shaft input jne instl3 jmp instl5 instl3: ;don't know what kind of input, wait for more input mov newinp, 0 ;clear flag jmp instl1 instl4: ;got button input, do action mov newinp, 0 ;clear flag call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jz inst4a ;pressed enter, go on to load game into SRAM & play push ax ;inst4e: mov ah, lastthree ;shift game into last 4 list mov lastfour, ah mov ah, lasttwo mov lastthree, ah mov ah, lastone mov lasttwo, ah mov lastone, al mov con1st, al ;select bank out CONTROL1, al and con2st, 10000011b ;mask off rom select, turn off atari, turn backlight on test al, 10000000b ;check if upper or lower half jz inst4f or con2st, 10101111b ;lower half, setup for lower rom & cart off &buzzer off jmp inst4g inst4f: or con2st, 10101011b ;upper half, setup for upper rom & cart off & buzzer off inst4g: mov al, con2st out CONTROL2, al call blockc ;copy game into game sram call ssoff ;turn off screensaver & restart atari. pop ax mov newinp, 0 ;clear out any input that may have happened. jmp instl1 ;wait for more input inst4a: test lastui, 00000010b ;check if pressed back jz inst4b ;pressed back, return cmp ininsta, 1 ;check if in instant play routine jne inst4a2 jmp instantinstlstbackreturn inst4a2: jmp instl8 inst4b: test lastui, 00000001b ;check if pressed eject jz inst4c call ejectct ;release cart jmp instl1 ;wait for more input inst4c: test lastui, 00011000b ;check if pressed instant jz inst4d cmp ininsta, 1 jne inst4c1 jmp instantinstlstbackentry inst4c1: call instantp jmp instl0 ;wait for more input inst4d: jmp game1 ;don't know what input, wait again instl5: ;got shaft input, update instructions mov newinp, 0 ;clear flag call beep2 cmp shftnum, 65 ;check if moved up jl instl7 instl6: ;moved down, move list down by adjusting bx to point beyond next ; NL and redisplay screen. inc linenum ;increase the current line of text block mov cx, bx ;save current line pointer in case last line cmp linenum, 1 ;check if staying at top of instructions. je inst6b ;if so, don't need to adjust bx inst6a: mov ah, es:[bx] ;get current character inc bx ;move to next character cmp ah, NL ;check if found next line je inst6b cmp ah, NULL ;check if ran into end of text je inst6c jmp inst6a ;didn't find NL or NULL, check next character inst6c: mov bx, cx ;already was on last line, restore bx dec linenum ;restore current line inst6b: mov shftnum, 65 ;clear out shaft position jmp instl0 ;done adjusting bx, go on to redisplay instl7: ;moved up, move list up by adjusting bx to point to the character ; after the next previous NL or NULL cmp linenum, 0 ;check if already at top of text block je inst7a dec linenum ;decrease current line of text block cmp linenum, 0 ;check if at top of instructions je inst7a dec bx ;go past end of previous line. inst7b: dec bx ;move to previous character mov ah, es:[bx] ;get character cmp ah, NULL ;check if reached beginning of these ; instructions(ran into previous NULL) je inst7c cmp ah, NL ;check if ran into previous NL je inst7c jmp inst7b ;check next character inst7c: inc bx ;ran into previous NL or NULL, adjust bx inst7a: mov shftnum, 65 ;clear out shaft position. jmp instl0 ;done adjusting bx, go on to redisplay instl8: pop bx ;restore and exit mov shftnum, bh mov linenum, bl pop es pop cx pop bx pop ax ret instlst endp ;***************************************************************; ;** Instant play procedure **; ;***************************************************************; ;instant play procedure ;destroys nothing, but alters the control registers to play. instantp proc push ax push bx push cx push es mov bl, linenum ;save screen position from caller mov bh, shftnum push bx instantinstlstbackentry: test lastui, 00001000b ;check if pressed "Last Game" jz instant1 ;pressed "Last Game" mov al, lastone ;load the last game mov ah, lasttwo ;shift it out of last 4 list mov lastone, ah mov ah, lastthree mov lasttwo, ah mov ah, lastfour mov lastthree, ah jmp instant4 ;go on to instant play it. instant1: ;must have pressed "Random Game" mov al, randnum ;get a random number mov ah, lastthree ;shift it into last 4 list mov lastfour, ah mov ah, lasttwo mov lastthree, ah mov ah, lastone mov lasttwo, ah mov lastone, al instant4: mov con1st, al ;select bank out CONTROL1, al and con2st, 10000011b ;mask off rom select, turn off atari, turn backlight on test al, 10000000b ;check if upper or lower half jz instant2 or con2st, 10101111b ;upper half, setup for upper rom & cart off & buzzer off jmp instant3 instant2: or con2st, 10101011b ;lower half, setup for lower rom & cart off & buzzer off instant3: mov al, con2st out CONTROL2, al call ssoff ;turn off screensaver & restart atari. mov al, con1st ;load the selected game number mov ininsta, 1 ;flag that in instant play jmp instlstinstantbackentry instantinstlstbackreturn: mov ininsta, 0 ;flag not in instant play pop bx ;restore and exit mov shftnum, bh mov linenum, bl pop es pop cx pop bx pop ax ret instantp endp ;***************************************************************; ;** Load cartridge procedure **; ;***************************************************************; ;Load cartridge data into memory procedure ;destroys nothing loadcart proc push ax push bx push cx push es mov al, con2st test al, 00000001b ;check if already on screensaver jnz load1 call sson ;otherwise, turn it on. load1: and al, 10100011b ;mask off rom select or al, 10000001b ;setup for cart & turn off buzzer mov con2st, al out CONTROL2, al test lastui, 10000000b ;check if cartridge inserted. jz load6 ;if cart inserted, go on. ;no cartridge inserted, warn user and check for override call clrlcd ;clear lcd& goto 1,1 mov bx, seg loadwarn mov es, bx mov bx, offset loadwarn call disps ;display warning message mov newinp, 0 ;clear any previous input mov cx, 100 ;wait for 2sec load2: push cx mov cx, 10000 call delay ;wait for 2us * 10000 pop cx cmp newinp, 0 jne load3 ;any user input will stop 2sec wait. loop load2 jmp load7 ;no button press, exit load3: mov newinp, 0 ;clear input test lastui, 00000100b ;check if pressed enter jz load7 ;if didn't press enter, exit call beep1 ;beep in response to button load6: mov al, con2st and al, 11111101b ;turn on cartridge socket mov con2st, al out CONTROL2, al call blockc ;copy cartridge into game sram or al, 10000010b ;turn off cartridge socket &buzzer off mov con2st, al out CONTROL2, al load7: pop es pop cx pop bx pop ax ret loadwarn: db ' Warning!', NL db ' No cartridge found.', NL db NL db ' (Press enter to override)', NULL loadcart endp ;***************************************************************; ;** Edit Memory Procedure **; ;***************************************************************; ;Edit memory menu ;destroys nothing editmem proc push ax push bx push cx push dx push es mov bl, linenum ;save screen position from caller mov bh, shftnum push bx mov shftnum, 65 ;set pointer to first mov curadr, 0000h ;start at beginning of memory block editmem0: mov al, con2st ;check if need to re-aquire gamebus test al, 00000001b ;test if bus in 188 mode jnz editmem00 mov ax, seg gamebus ;load gamebus's address mov es, ax mov ax, offset gamebus mov di, ax call sson ;get possesion of gamebus mov al, con2st ;setup to use sram and al, 11100011b ;mask off chip select or al, 00000111b ;select sram mov con2st, al out CONTROL2, al editmem00: call clrlcd ;clear screen (and goto 1,1) mov bx, curadr mov cx, 4 ;print 4 lines editmem0b: push cx mov ah, 5 ;goto the correct line mov al, cl sub ah, cl mov al, 2 call gotoxy mov ax, bx call dispadr mov cx, bytesper ;print number of bytes per line editmem0a: mov al, es:[bx][di] ;get a byte call dispbyte inc bx ;move to next byte cmp bx, 0FFFh ;check if last byte jle editmem0a1 mov cx, 1 ;end loop. editmem0a1: loop editmem0a pop cx cmp bx, 0FFFh ;check if last byte jle editmem0a2 mov cx, 1 ;end loop. editmem0a2: loop editmem0b mov ah, shftnum ;load pointer position sub ah, 64 ;adjust for y coord mov al, 1 ;first column call gotoxy mov al, arrow ;print arrow call dispc ;wait until get new input editmem1: hlt cmp newinp, 0 je editmem1 push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne editmem2 jmp editmem4 editmem2: cmp newinp, 02h ;check if new shaft input jne editmem3 jmp editmem5 editmem3: ;don't know what kind of input, wait for more input mov newinp, 0 ;clear flag jmp editmem1 editmem4: ;got button input, do action mov newinp, 0 ;clear flag call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jnz editmem4a0 jmp editmem4a editmem4a0: ;pressed enter mov bl, 1 ;bl will be byte on line working on mov cx, bytesper ;number of bytes to go through mov bh, 3 ;constant for multiplying editmem4a1: mov al, bl ;load pointer position column mul bh add al, 2 mov ah, shftnum ;load pointer position, row sub ah, 64 ;adjust for y coord push bx push ax ;save arrow location call gotoxy mov al, arrow ;print arrow call dispc dec bl ;find byte working on xor bh, bh add bx, curadr push cx mov cl, shftnum ;load line position sub cl, 65 ;adjust xor ch, ch cmp cx, 0 je editmem4a3 editmem4a2: add bx, bytesper loop editmem4a2 editmem4a3: pop cx mov dl, es:[bx][di] ;get byte pop ax ;pass in lcd location push ax inc al ;adjust location for byte(instead of arrow) call adjustbyte ;change byte mov es:[bx][di], dl ;put new byte in pop ax pop bx inc bl call gotoxy mov al, ' ' ;blank out arrow call dispc loop editmem4a1 jmp editmem0 ;wait for more input editmem4a: test lastui, 00000010b ;check if pressed back jz editmem4b ;pressed back jmp editmem8 ;pressed back, return editmem4b: test lastui, 00000001b ;check if pressed eject jz editmem4c ;pressed eject call ejectct ;release cart jmp editmem1 ;wait for more input editmem4c: test lastui, 00011000b ;check if pressed instant jz editmem4d ;pressed instant call instantp jmp editmem0 ;wait for more input editmem4d: jmp editmem1 ;don't know what input, wait again editmem5: ;got shaft input, update list mov newinp, 0 ;clear flag call beep2 cmp shftnum, 65 ;check if moved past top jl editmem6 cmp shftnum, 68 jg editmem7 ;pointer must still be on screen, just redisplay jmp editmem0 editmem6: ;moved above screen top, move list up. cmp curadr, 0 je editmem6a sub curadr, bytesper editmem6a: mov shftnum, 65 jmp editmem0 editmem7: ;moved below screen bottom, move list down. add curadr, bytesper cmp curadr, 0FFFh ;check if went past end jl editmem7a sub curadr, bytesper ;if went past, correct editmem7a: mov shftnum, 68 jmp editmem0 editmem8: pop bx mov linenum, bl mov shftnum, bh pop es pop dx pop cx pop bx pop ax ret editmem endp ;***************************************************************; ;** Display byte Procedure **; ;***************************************************************; ;displays in hex byte in al to lcd at current location ;destroys nothing dispbyte proc push ax mov ah, al shr al, 4 ;'mask' upper nibble add al, '0' ;convert to ascii cmp al, '9' jle dispbyte1 add al, ('A' - '9' - 1) dispbyte1: call dispc ;print upper nibble mov al, ah ;get lower nibble and al, 0Fh ;mask off least sig nibble add al, '0' ;convert to ascii cmp al, '9' jle dispbyte2 add al, ('A' - '9' - 1) dispbyte2: call dispc ;print least sig. nibble mov al, ' ' ;print space call dispc pop ax ret dispbyte endp ;***************************************************************; ;** Display address Procedure **; ;***************************************************************; ;displays in hex 12bit address in ax to lcd at current location ;destroys nothing dispadr proc push ax push bx mov bx, ax mov al, ah and al, 0Fh ;mask off lower nibble add al, '0' ;convert to ascii cmp al, '9' jle dispadr1 add al, ('A' - '9' - 1) dispadr1: call dispc ;print most significant nibble mov al, bl ;get middle nibble shr al, 4 ;'mask' middle nibble add al, '0' ;convert to ascii cmp al, '9' jle dispadr2 add al, ('A' - '9' - 1) dispadr2: call dispc ;print middle nibble mov al, bl ;get least sig. nibble and al, 0Fh ;mask off least sig nibble add al, '0' ;convert to ascii cmp al, '9' jle dispadr3 add al, ('A' - '9' - 1) dispadr3: call dispc ;print least sig. nibble mov al, ' ' ;print space call dispc pop bx pop ax ret dispadr endp ;***************************************************************; ;** adjust byte Procedure **; ;***************************************************************; ;adjusts byte in dl at lcd location in ax(as per gotoxy) ;returns new byte in dl ;destroys dx adjustbyte proc push ax push bx mov bl, shftnum push bx mov bx, ax ;bx will save location on lcd mov dh, dl ;dh will save original byte ;dl will be current version of byte mov shftnum, 65 ;set pointer to first adjustbyte0: mov ax, bx ;load lcd location call gotoxy ;goto byte location on lcd mov al, dl ;load byte call dispbyte ;print current byte adjustbyte1: ;wait for input hlt cmp newinp, 0 je adjustbyte1 push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT ;keep backlight on xor ax, ax out dx, al pop dx ;check type of input cmp newinp, 01h ;check if new button input jne adjustbyte2 jmp adjustbyte4 adjustbyte2: cmp newinp, 02h ;check if new shaft input jne adjustbyte3 jmp adjustbyte5 adjustbyte3: ;don't know what kind of input, wait for more input mov newinp, 0 ;clear flag jmp adjustbyte1 adjustbyte4: ;got button input, do action mov newinp, 0 ;clear flag call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jz adjustbyte4a ;pressed enter jmp adjustbyte8 ;accept current byte, and leave. adjustbyte4a: test lastui, 00000010b ;check if pressed back jz adjustbyte4b ;pressed back mov dl, dh ;restore original byte jmp adjustbyte8 ;leave adjustbyte4b: test lastui, 00000001b ;check if pressed eject jz adjustbyte4c ;pressed eject call ejectct ;release cart jmp adjustbyte1 ;wait for more input adjustbyte4c: test lastui, 00011000b ;check if pressed instant jz adjustbyte4d ;pressed instant ;can't re-display screen, just ignore ; the instant button jmp adjustbyte1 ;wait for more input adjustbyte4d: jmp adjustbyte1 ;don't know what input, wait again adjustbyte5: ;got shaft input, update value mov newinp, 0 ;clear flag call beep2 cmp shftnum, 65 ;check if moved up jl adjustbyte6 jmp adjustbyte7 ;otherwise, must have moved down adjustbyte6: ;moved up, increase current value cmp dl, 0FFh ;check if value maxed je adjustbyte6a inc dl adjustbyte6a: mov shftnum, 65 jmp adjustbyte0 adjustbyte7: ;moved down, decrease current value cmp dl, 00h ;check if value min'ed je adjustbyte7a dec dl adjustbyte7a: mov shftnum, 65 jmp adjustbyte0 adjustbyte8: ;print byte before leaving mov ax, bx ;load lcd location call gotoxy ;goto byte location on lcd mov al, dl ;load byte call dispbyte ;print current byte pop bx mov shftnum, bl pop bx pop ax ret adjustbyte endp ;***************************************************************; ;** Setup Procedure **; ;***************************************************************; ;Setup Menu ;destroys nothing setup proc push ax push bx push cx push dx push es mov bl, linenum ;save screen postion of caller mov bh, shftnum push bx mov shftnum, 65 ;set pointer to first mov linenum, 0 ;set list to start at first setup0: ;display setup menu. call clrlcd ;clear screen (and goto 1,1) mov ax, seg setuptxt ;display menu mov es, ax mov bx, offset setuptxt mov al, linenum ;load current line number mov cl, 41 ;multiply it by 40 to find offset (from mul cl ; the begining of text) for line to print add bx, ax ;compute address to start printing call disps ;print menu mov ah, shftnum ;goto the pointer line sub ah, 64 ;row mov al, 1 ;column call gotoxy mov ax, seg setupptr ;display pointer mov es, ax mov bx, offset setupptr call disps ;display the current variables. ;speed mov ah, linenum cmp ah, 0 ;check if should print this data jg setup0a inc ah mov al, 33 call gotoxy ;goto the printing location mov al, 10 sub al, shftdiv ;compute 'speed'. Speed: ; 9 = fastest corresponding to shftdiv=1 ; 0 = slowest corresponding to shftdiv=10 add al, '0' ;convert to ascii digit call dispc ;print the current variable ;click length setup0a: mov ah, linenum cmp ah, 1 ;check if should print this data jg setup0b mov ah, 2 ;compute row to print on sub ah, linenum mov al, 33 ;column call gotoxy ;goto printing location mov ax, beeplen2 ;print data call dispdec ;print the number ;beep length setup0b: mov ah, linenum cmp ah, 2 ;check if should print this data jg setup0c mov ah, 3 ;compute row to print on sub ah, linenum mov al, 33 ;column call gotoxy ;goto printing location mov ax, beeplen1 ;print data call dispdec ;print the number ;reset time setup0c: mov ah, linenum cmp ah, 3 ;check if should print this data jg setup0d mov ah, 4 ;compute row to print on sub ah, linenum mov al, 33 ;column call gotoxy ;goto printing location mov ax, resettime ;print data call dispdec ;backlight sleep time setup0d: mov ah, linenum cmp ah, 0 ;check if should print this data je setup1 mov ah, 5 sub ah, linenum mov al, 33 ;column call gotoxy mov ax, sleep ;printdata call dispdec ;wait until get new input setup1: hlt cmp newinp, 0 je setup1 push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne setup2 jmp setup4 setup2: cmp newinp, 02h ;check if new shaft input jne setup3 jmp setup5 setup3: mov newinp, 0 jmp setup1 ;no new input to act on,wait more ;got new button input, do action. setup4: mov newinp, 0h ;clear flag. call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jnz setup4a jmp setup4b setup4a: ;pressed enter, go on to do action. mov ah, shftnum ;goto the pointer line sub ah, 64 ;row mov al, 30 ;column call gotoxy mov ax, seg setupptr ;display sub-pointer mov es, ax mov bx, offset setupptr call disps mov al, shftnum ;compute the line pointer on. sub al, 64 add al, linenum cmp al, 1 jne setup4a1 ;setup knob speed mov al, 10 sub al, shftdiv ;compute 'speed' mov ah, 80h ;set flag call setdec mov ah, 10 ;un-compute 'speed' sub ah, al mov shftdiv, ah jmp setup0 setup4a1: cmp al, 2 jne setup4a2 ;setup click length mov ax, beeplen2 call setdec mov beeplen2, ax jmp setup0 setup4a2: cmp al, 3 jne setup4a3 ;setup beep length mov ax, beeplen1 call setdec mov beeplen1, ax jmp setup0 setup4a3: cmp al, 4 jne setup4a4 ;setup Atari reset time mov ax, resettime call setdec mov resettime, ax jmp setup0 setup4a4: cmp al, 5 jne setup4a5 ;setup backlight delay time mov ax, sleep call setdec cmp ax, 327 ;check if above maximum time jle setup4a4a mov ax, 327 setup4a4a: mov sleep, ax mov dx, 200 ;multiply by 200 to set timer mul dx mov dx, T0CMPA out dx, al jmp setup0 setup4a5: cmp al, 6 jne setup4a6 ;reset? ;wait until get new input setup4a5a: hlt cmp newinp, 0 je setup4a5a push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax call beep1 mov newinp, 0 test lastui, 00000100b ;check if pressed enter jnz setup4a5b jmp setup0 ;cancel with anyother input setup4a5b: jmp start ;jump to first code setup4a6: jmp setup1 ;wait for more input setup4b: ;check if pressed back test lastui, 00000010b jz setup4c jmp setup6 ;pressed back, return setup4c: ;check if pressed eject test lastui, 00000001b jz setup4d call ejectct ;release cart jmp setup1 ;wait for more input setup4d: ;check if pressed instant test lastui, 00011000b jz setup4e call instantp jmp setup0 ;wait for more input setup4e: ;don't know what input, wait again jmp setup1 ;got new shaft input, screen setup5: mov newinp, 0h ;clear flag call beep2 mov al, shftnum cmp al, 65 ;check if moved before first line jl setup5a cmp al, 68 ;check if moved after last line jg setup5c jmp setup0 ;still on screen ;moved before first line, pointer to line 1, and decrement ; the linenum in list(unless already at beginning) setup5a: mov shftnum, 65 cmp linenum, 0 je setup5b dec linenum setup5b: jmp setup0 ;moved after last line, pointer to line 4, increment linenum(unless at end) setup5c: mov shftnum, 68 cmp linenum, 2 ;last item to be at top. je setup5b inc linenum jmp setup5b setup6: pop bx mov shftnum, bh mov linenum, bl pop es pop dx pop cx pop bx pop ax ret ;menu text; Note that each line is exactly 41 bytes long ; pointer goes at columns 1 and 30 setuptxt: db ' Set knob speed ', NL db ' Set click length . ms ', NL db ' Set beep length . ms ', NL db ' Set Atari reset time . sec', NL db ' Set backlight sleep time . min', NL db ' Reset system Reset? ', NL db NULL setupptr: db '-', arrow, NULL setup endp ;***************************************************************; ;** Set decimal number procedure **; ;***************************************************************; ;takes the value in ax, and uses shaft encoder to adjust it from ; 0 to 999(decimal = 3E7hex). returns the updated value in ax. ;If the MSB of ah is 1(outside of above range), then use al and only ; do single digit from 0 to 9 and return it in al ;pressing enter sets the value(returns new value), pressing back ; restores the old value and exits. ;intended to be used with setup procedure, as it calls dispdec. ;destroys ax setdec proc push bx mov bl, linenum ;save screen postion of caller mov bh, shftnum push bx push ax setdec0: test ah, 80h ;check if doing 1 digit, or 3 jnz setdec0a call dispdec ;print current value jmp setdec1 setdec0a: ;print only 1 digit mov bx, ax ;save digit mov ah, LCDline ;goto proper location mov al, 33 call gotoxy mov ax, bx ;restore digit add al, '0' ;adjust for ascii digit call dispc ;print digit mov ax, bx ;restore digit ;wait until get new input setdec1: hlt cmp newinp, 0 je setdec1 push ax push dx mov al, con2st ;turn on backlight and al, 10111111b mov con2st, al out CONTROL2, al mov dx, T0CNT xor ax, ax out dx, al pop dx pop ax ;check type of input cmp newinp, 01h ;check if new button input jne setdec2 jmp setdec4 setdec2: cmp newinp, 02h ;check if new shaft input jne setdec3 jmp setdec5 setdec3: mov newinp, 0 jmp setdec1 ;no new input to act on,wait more ;got new button input, do action. setdec4: mov newinp, 0h ;clear flag. call beep1 ;beep in response to button test lastui, 00000100b ;check if pressed enter jz setdec4b ;pressed enter, keep the new number pop bx ;get rid of old value push ax ;put in new one jmp setdec6 ;exit setdec4b: ;check if pressed back test lastui, 00000010b jz setdec4c jmp setdec6 ;pressed back, return with old value setdec4c: ;check if pressed eject test lastui, 00000001b jz setdec4d call ejectct ;release cart jmp setdec1 ;wait for more input setdec4d: ;check if pressed instant test lastui, 00011000b jz setdec4e call instantp jmp setdec0 ;wait for more input setdec4e: ;don't know what input, wait again jmp setdec1 ;got new shaft input, adjust number, and reprint setdec5: mov newinp, 0h ;clear flag call beep2 mov bl, shftnum cmp bl, 65 ;check if moved up jl setdec5a cmp bl, 65 ;check if moved down jg setdec5c jmp setdec0 ;still on screen ;moved up, increase the current value(unless at max 999=3E7h; or 9 for 1 digit) setdec5a: mov shftnum, 65 test ah, 80h ;check if 1 digit adjustment jnz setdec5d cmp ax, 03E7h jge setdec5b inc ax setdec5b: jmp setdec0 setdec5d: cmp al, 09h jge setdec5e inc al setdec5e: jmp setdec0 ;moved down, decrease the current value(unless at min 000) setdec5c: mov shftnum, 65 test ax, 7FFFh jz setdec5b dec ax jmp setdec0 setdec6: pop ax pop bx mov shftnum, bh mov linenum, bl pop bx ret setdec endp ;***************************************************************; ;** Display decimal number procedure **; ;***************************************************************; ;prints the number in ax at LCD location specifially for setup procedure ;hard codes in the columns, and uses current line to determine row ;prints 3 digits with least significat on right side of decimal point: ; 123 would be printed as '12.3' ;leaves LCD location at middle digit ;CAUTION! ax must be <= 2550, otherwise will have division overflow ;destroys nothing dispdec proc push ax push bx push cx mov cx, ax mov ah, LCDline ;row to goto mov al, 36 ;column call gotoxy ;go to print least significant digit mov ax, cx mov bl, 10 ;compute least significant digit div bl add ah, '0' ;convert least sig. dig. to ascii number mov cx, ax mov al, ah call dispc ;print least sig. dig. mov ah, LCDline ;row to goto mov al, 34 ;column call gotoxy ;go to print next digit mov ax, cx xor ah, ah ;get rid of remainder div bl ;compute next digit add ah, '0' ;convert digit to ascii number mov cx, ax mov al, ah call dispc ;print digit mov ah, LCDline ;stay on same row mov al, 33 ;column call gotoxy ;go to print next digit mov ax, cx xor ah, ah ;get rid of remainder div bl ;compute next digit add ah, '0' mov al, ah call dispc pop cx pop bx pop ax ret dispdec endp ;***************************************************************; ;** Eject Cartidge procedure **; ;***************************************************************; ;releases the cartridge for user removal: ; turns on screensaver, resets atari, turns off cartridge ;destroys nothing ejectct proc push ax ; call sson ;turn on screensaver and release cartridge mov al, con2st ;load current control2 status or al, 10000011b ;turn off cartridge & buzzer off out CONTROL2, al mov con2st, al call reset pop ax ret ejectct endp ;***************************************************************; ;** Delay procedure **; ;***************************************************************; ;delay for about 2us * contents of cx ;destroys cx delay proc twous: loop twous ;each loop is about 16 cycles. ;with 1 more wait state.(UCS) ; (16+1) * 125ns/cycle = 2.1us ret delay endp ;***************************************************************; ;** Goto an xy position on LCD procedure **; ;***************************************************************; ;sets the current address on the LCD display ;x coordinate(column) in al ;y coordinate(row) in ah ;x(al) is from 1 to 40 (decimal) ;y(ah) is from 1 to 4 ;Unpredicable results may occur when x & y are out of these ranges. ;handles all peculiarities with the 2 module controllers ;destorys nothing gotoxy proc push ax mov LCDline, ah ;store line dec al ;adjust xcoord for LCD command or al, 80h cmp ah, 1 je gotox3 cmp ah, 3 je gotox3 or al, 0C0h gotox3: push ax cmp ah, 2 ;check which module half to use jg gotox2 gotox1: ;lines 1 or 2 in al, LCD1IR ;wait till ready test al, 80h jnz gotox1 pop ax out LCD1IR, al ;goto xy coord pop ax ret gotox2: ;lines 3 or 4 in al, LCD2IR ;wait till ready test al, 80h jnz gotox2 pop ax out LCD2IR, al ;goto xy coord pop ax ret gotoxy endp ;***************************************************************; ;** Clear LCD procedure **; ;***************************************************************; ;Clears the LCD, and sets current address to col1, row 1 ;handles all peculiarities with the 2 module controllers ;Procedure time is negligable, but causes a 1.6ms delay in module! ;destroys nothing clrlcd proc push ax mov LCDline, 1 ;go to line 1 ;lines 1&2 (controller 1) clrlc1: in al, LCD1IR ;wait till ready test al, 80h jnz clrlc1 mov al, 01h ;clear LCD out LCD1IR, al ;lines 3&4 (controller 2) clrlc2: in al, LCD2IR ;wait till ready test al, 80h jnz clrlc1 mov al, 01h ;clear LCD out LCD2IR, al pop ax ret clrlcd endp ;***************************************************************; ;** Display character on LCD procedure **; ;***************************************************************; ;displays character in al on LCD at current address. ;Procedure takes about time: ; (19+14+10+13+13+10+13+14+12+22)*125ns ~= 20us ; But delays LCD for another 40us ;handles all peculiarities with the 2 module controllers ;destroys nothing dispc proc push ax cmp LCDline, 2 jg dcbsy2 ;check which line dcbsy1: in al, LCD1IR ;wait till ready ;lines 1&2 test al, 80h jnz dcbsy1 pop ax ;restore character out LCD1DR, al ;send character ret dcbsy2: in al, LCD2IR ;wait till ready ;lines 3&4 test al, 80h jnz dcbsy2 pop ax ;restore character out LCD2DR, al ;send character ret dispc endp ;***************************************************************; ;** Display string on LCD procedure **; ;***************************************************************; ;displays string pointed to by es:bx on LCD at current address. ;Strings must be NULL terminated. ;Caution should be taken on the part of the caller: ; Unintended operation may occur if text goes past last column ; NL character will cause the current address to assigned to the ; first column of the next line. If on last line, will be ; assigned to first line, and printing will be discontinued ; regardless of how many more characters there are. ; Does not clear old text, some old text may remain on screen. ;handles all peculiarities with the 2 module controllers ;calls dispc for each character ;destroys nothing disps proc push ax push bx disps3: mov al, es:[bx] cmp al, NULL ;check if string done. je disps1 cmp al, NL ;check if new line. je disps2 call dispc ;must be character, print it inc bx jmp disps3 ;go on to do next character disps2: ;newline, go to next line mov al, 1 ;column 1 mov ah, LCDline inc ah ;move to next line cmp ah, 5 ;check if moved past last line je disps4 call gotoxy ;go there inc bx ;go on to next character jmp disps3 disps4: mov ah, 1 ;if so, go to first line. call gotoxy disps1: pop bx pop ax ret disps endp ;***************************************************************; ;** screensaver on procedure **; ;***************************************************************; ;Puts the game bus into 188 mode, enables the screen saver, and ; resets the atari ;destroys nothing sson proc push ax mov al, con2st ;load current control2 status or al, 00000001b ;set for screensaver enabled out CONTROL2, al mov con2st, al call reset pop ax ret sson endp ;***************************************************************; ;** screensaver off procedure **; ;***************************************************************; ;Puts the game bus into atari mode, disables the screen saver, and ; resets the atari ;destroys nothing ssoff proc push ax mov al, con2st ;load current control2 status and al, 11111110b ;set for screensaver disabled mov con2st, al out CONTROL2, al call reset pop ax ret ssoff endp ;***************************************************************; ;** reset procedure **; ;***************************************************************; ;resets the atari ;alters CONTROL2 to power cycle the atari ;destroys nothing reset proc push ax push cx cmp resettime, 0 ;check if zero time je reset2 mov al, con2st and al, 11011111b ;turn off atari mov con2st, al out CONTROL2, al mov cx, resettime ;delay for about resettime/10 seconds reset1: push cx mov cx, 50000 call delay pop cx loop reset1 or al, 00100000b ;turn on atari mov con2st, al out CONTROL2, al reset2: pop cx pop ax ret reset endp ;***************************************************************; ;** Game block copy procedure **; ;***************************************************************; ;Copies a game from ROM into the game SRAM. ;game to be copied must already be selected via control1 & 2 ;Alters CONTROL2, but restores. Gamebus should be in 188 mode when called. ;destroys di, si blockc proc push ax ;store state push bx push cx push es mov al, con1st mov ah, con2st push ax mov cx, 1000h ;copy 4096 bytes mov bx, 0 mov ax, seg gamebus ;load gamebus's address mov es, ax mov ax, offset gamebus mov di, ax block1: mov al, con2st ;load original source out CONTROL2, al mov ah, es:[bx][di] ;get a byte push ax ;save the byte, and the original CONTROL2 status and al, 11100011b ;mask off chip select or al, 00000100b ;select sram out CONTROL2, al pop ax ;restore the byte &original CONTROL2 status mov es:[bx][di], ah inc bx loop block1 pop ax mov con1st, al ;restore CONTROL1 out CONTROL1, al ;re-select the original game mov al, ah mov con2st, al ;restore CONTROL2 out CONTROL2, al ;re-select the original source pop es pop cx pop bx pop ax ret blockc endp ;***************************************************************; ;** Timer 1 interupt service routine **; ;***************************************************************; ;Poll for user input ;sets lastui variable to incate most recent user input, and newinp to flag ;destroys nothing tm1isr proc push ax ;store state push bx push dx inc randnum ;continuously generate random numbers cmp buttondb, 0 je tm1is3 dec buttondb ;decrement the wait counter tm1is3: in al, USERINPUT ;get newest input. mov bh, al xor bh, lastui ;check if anything changed. cmp bh, 00h jne tm1is5 ;if zero, no new input, leave. jmp tm1is1 tm1is5: test al, 00011111b ;check if button depresed. jz tm1is2 ;if zero, go on to test shaft encoder mov bh, al mov bl, lastui and bl, 00011111b ;mask off button bits and bh, 00011111b ;mask off button bits xor bh, bl ;check if buttons changed cmp bh, 00h je tm1is2 ;no button change, goto shaft encoder cmp buttondb, 0 ;check if have to wait for debouncing jne tm1is1 ;if need to wait, leave mov newinp, 1 ;New button input, set new input flag mov buttondb, 1 ;wait once to debounce jmp tm1is1 ;done. tm1is2: test bh, 10000000b ;check if just was cartridge input jnz tm1is1 ;if was new cart input, leave(don't care about ; cartridge input here, routines check it) mov ah, lastui ;check shaft encoder and ah, 01100000b ;mask off encoder bits shr ah, 5 ;move over to end mov bh, al ;newest input and bh, 01100000b ;mask off encoder bits shr bh, 3 ;move over or bh, ah ;make compsite for easier testing. cmp bh, 01h ;check if CW jne tm1is4 dec shftcnt jnz tm1is1 ;if not down to zero, don't count mov dl, shftdiv ;reload shftcnt mov shftcnt, dl inc shftnum ;CW mov newinp, 2 ;new shaft input jmp tm1is1 tm1is4: cmp bh, 02h ;check if CCW jne tm1is1 dec shftcnt jnz tm1is1 ;if not down to zero, don't count mov dl, shftdiv ;reload shftcnt mov shftcnt, dl dec shftnum ;CCW mov newinp, 2 ;new shaft input jmp tm1is1 ;Note: actually only counting by 4 of the encoders divisions. The ; encoder has 1024 divisions per rotation, which is way more than ; needed. Only record every forth increment for 256 divisions per ; rotation. tm1is1: mov lastui, al ;save newest input as last input mov dx, EOI ;clear inservice reg.(if used) mov ax, 8000h ;non-specific out dx, al pop dx ;restore state. pop bx pop ax sti iret tm1isr endp ;***************************************************************; ;** Timer 0 interupt service routine **; ;***************************************************************; ;turns off the backlight ;destroys nothing tm0isr proc push ax ;store state push dx mov al, con2st or al, 01000000b ;turn off backlight mov con2st, al out CONTROL2, al mov dx, EOI ;clear inservice reg.(if used) mov ax, 8000h ;non-specific out dx, al pop dx ;restore state. pop ax sti iret tm0isr endp ;***************************************************************; ;** Beep2 procedure **; ;***************************************************************; ;beep for the shaft encoder at length according to beeplen2 variable ;destroys nothing beep2 proc push cx mov cx, beeplen2 ;load length to beep call beep pop cx ret beep2 endp ;***************************************************************; ;** Beep1 procedure **; ;***************************************************************; ;beep for the button input at length according to beeplen1 variable ;destroys nothing beep1 proc push cx mov cx, beeplen1 ;load length to beep call beep pop cx ret beep1 endp ;***************************************************************; ;** Beep procedure **; ;***************************************************************; ;Pulse the buzzer quickly for a beep corresponding to 0.1ms length in cx. ;intended to be called only by beep1 & beep2 ;destroys cx beep proc push ax cmp cx, 0 je beep0a ;if zero time, don't beep. mov al, con2st and al, 01111111b ;turn on buzzer mov con2st, al out CONTROL2, al beep0: push cx mov cx, 40 ;make this loop take approx 0.1ms call delay pop cx loop beep0 mov al, con2st or al, 10000000b ;turn off buzzer mov con2st, al out CONTROL2, al beep0a: pop ax ret beep endp ;***************************************************************; ;** Initialize LCD procedure **; ;***************************************************************; ;Initialize the LCD module for all 4 lines(2 inits) ;procedure is per LCD specs ;destroys cx, ax initdisplay proc ;lines 1&2 mov cx, 10000 ;wait 20ms call delay mov al, 38h ;init: 8bit, 2lines, dotmatrix out LCD1IR, al mov cx, 2500 ;wait 5ms call delay mov al, 38h ;re-init out LCD1IR, al mov cx, 75 ;wait 150us call delay mov al, 38h ;re-init out LCD1IR, al mov cx, 25 ;wait 50us call delay mov al, 38h ;re-init out LCD1IR, al inbsy1: in al, LCD1IR ;wait till ready. test al, 80h jnz inbsy1 mov al, 00000110b ;increment, no shift out LCD1IR, al inbsy2: in al, LCD1IR ;wait till ready. test al, 80h jnz inbsy2 mov al, 00001100b ;display on, cursor off, no blink out LCD1IR, al inbsy3: in al, LCD1IR ;wait till ready test al, 80h jnz inbsy3 mov al, 00000001b ;clear display out LCD1IR, al inbsy4: in al, LCD1IR test al, 80h jnz inbsy4 mov al, 10000000b ;select first display location out LCD1IR, al ;lines 3&4 mov cx, 10000 ;wait 20ms call delay mov al, 38h ;init: 8bit, 2lines, dotmatrix out LCD2IR, al mov cx, 2500 ;wait 5ms call delay mov al, 38h ;re-init out LCD2IR, al mov cx, 75 ;wait 150us call delay mov al, 38h ;re-init out LCD2IR, al mov cx, 25 ;wait 50us call delay mov al, 38h ;re-init out LCD2IR, al inbsy5: in al, LCD2IR ;wait till ready. test al, 80h jnz inbsy5 mov al, 00000110b ;increment, no shift out LCD2IR, al inbsy6: in al, LCD2IR ;wait till ready. test al, 80h jnz inbsy6 mov al, 00001100b ;display on, cursor off, no blink out LCD2IR, al inbsy7: in al, LCD2IR ;wiat till ready test al, 80h jnz inbsy7 mov al, 00000001b ;clear display out LCD2IR, al inbsy8: in al, LCD2IR test al, 80h jnz inbsy8 mov al, 10000000b ;select first display location out LCD2IR, al ret initdisplay endp ;***************************************************************; ;** startup procedure **; ;***************************************************************; ;displays credits and welcome message, then waits for 1.5sec ;destroys ax, cx, dx startup proc push es call clrlcd ;clear LCD mov dx, seg credits1 mov es, dx mov bx, offset credits1 call disps ;display the intro/credits pop es mov newinp, 0 ;clear out input flag mov cx, 150 ;wait for 3sec start2: push cx mov cx, 10000 call delay ;wait for 2us * 10000 pop cx cmp newinp, 0 ;check if should get out jnz start3 loop start2 start3: mov newinp, 0 call clrlcd ret credits1: db ' Atari 2600 Game Server', NL db ' V1.0', NL db ' System copyright Mark De Smet', NL db ' Screen saver copyright John Harvey', NULL startup endp ;code ends ;***************************************************************; ;** bootup location **; ;***************************************************************; ; org 01FF0h ;location in ROM to place this ;;pwron segment at 0FFFFh ;power_on: ; mov dx, offset UCSST ; mov ax, eprom_start + 31h ;1 WAIT states in case use slow prom ; out dx, al ; jmp far ptr start ; ;;pwron ends code ends end start