/////////////////////////////////////////////////////////////////////////////////////////////////// // Handy constants. const FALSE = 0 const TRUE = !FALSE const NULL = 0 /////////////////////////////////////////////////////////////////////////////////////////////////// // Fixed memory locations const raycaster = $6000 // main mem const expandVec = $2000 // aux mem const fontEngine = $BA00 // main mem /////////////////////////////////////////////////////////////////////////////////////////////////// // Resource numbers const RES_NUM_RAYCASTER = 1 const RES_NUM_EXPAND_VEC = 2 const RES_NUM_FONT_ENGINE = 3 /////////////////////////////////////////////////////////////////////////////////////////////////// // Hardware addresses. const keyboard = $C000 const keystrobe = $C010 /////////////////////////////////////////////////////////////////////////////////////////////////// // Memory manager definitions // Resource types const RES_TYPE_CODE = 1 const RES_TYPE_2D_MAP = 2 const RES_TYPE_3D_MAP = 3 const RES_TYPE_TILE = 4 const RES_TYPE_TEXTURE = 5 const RES_TYPE_SCREEN = 6 const RES_TYPE_FONT = 7 const RES_TYPE_MODULE = 8 const RES_TYPE_BYTECODE = 9 const RES_TYPE_FIXUP = 10 // Memory banks const MAIN_MEM = 0 const AUX_MEM = 1 // Command codes const RESET_MEMORY = $10 const REQUEST_MEMORY = $11 const LOCK_MEMORY = $12 const UNLOCK_MEMORY = $13 const SET_MEM_TARGET = $14 const START_LOAD = $15 const QUEUE_LOAD = $16 const FINISH_LOAD = $17 const FREE_MEMORY = $18 const CALC_FREE = $19 const DEBUG_MEM = $1A const CHAIN_LOADER = $1E const FATAL_ERROR = $1F const callbacks = $300 const OVERMAP = 11 /////////////////////////////////////////////////////////////////////////////////////////////////// // Predefined functions, for circular calls predef moveBackward, setWindow2 /////////////////////////////////////////////////////////////////////////////////////////////////// // Raycaster variables word zp = 0 const playerDir = $5D const playerX = $5E const playerY = $60 const backBuf = $6A const frontBuf = $6B /////////////////////////////////////////////////////////////////////////////////////////////////// // Font engine variables const wndleft = $70 // left edge of the window const wndwdth = $71 // right edge (NOT width) of text window const wndtop = $72 // top of text window const wndbtm = $73 // bottom+1 of text window const cursh = $74 // Cursor H-pos 0-39 const cursv = $75 // Cursor V-pos 0-23 const MAX_LOC_TRIG = 128 /////////////////////////////////////////////////////////////////////////////////////////////////// // Strings. byte helloStr[] = "Loading Lawless Legends.\n" byte loopStr[] = "Entering keyboard loop.\n" byte tooManyTriggers[] = "Too many triggers" /////////////////////////////////////////////////////////////////////////////////////////////////// // Global variables byte mapNum = OVERMAP word pFont word pMap word pScripts word cmdTbl[64] byte nLocTrig byte locTrig_x[MAX_LOC_TRIG] byte locTrig_y[MAX_LOC_TRIG] word locTrig_func[MAX_LOC_TRIG] byte prevX byte prevY word prevScript byte prevMapNum byte redraw byte titleLoaded = FALSE byte cacheSky, cacheGround word cacheX, cacheY byte cacheDir byte resetLocFromCache = FALSE // Movement amounts when walking at each angle // Each entry consists of an X bump and a Y bump, in 8.8 fixed point word walkDirs[] = $0040, $0000 word = $003B, $0018 word = $002D, $002D word = $0018, $003B word = $0000, $0040 word = $FFE8, $003B word = $FFD3, $002D word = $FFC5, $0018 word = $FFC0, $0000 word = $FFC5, $FFE8 word = $FFD3, $FFD3 word = $FFE8, $FFC5 word = $0000, $FFC0 word = $0018, $FFC5 word = $002D, $FFD3 word = $003B, $FFE8 byte skyGndTbl1[] = $00 // lo-bit black byte = $00 // lo-bit black byte = $00 // lo-bit black byte = $02 // violet byte = $08 // green byte = $0A // lo-bit white byte = $0A // lo-bit white byte = $0A // lo-bit white byte = $20 // hi-bit black byte = $20 // hi-bit black byte = $20 // hi-bit black byte = $22 // blue byte = $28 // orange byte = $2A // hi-bit white byte = $2A // hi-bit white byte = $2A // hi-bit white byte skyGndTbl2[] = $00 // lo-bit black byte = $02 // violet byte = $08 // green byte = $02 // violet byte = $08 // green byte = $02 // violet byte = $08 // green byte = $0A // lo-bit white byte = $20 // hi-bit black byte = $22 // blue byte = $28 // orange byte = $22 // blue byte = $28 // orange byte = $22 // blue byte = $28 // orange byte = $2A // hi-bit white word skyNum = 9 word groundNum = 10 /////////////////////////////////////////////////////////////////////////////////////////////////// // Definitions used by assembly code asm __defs !source "../../include/global.i" !source "../../include/plasma.i" !source "../../include/mem.i" !source "../../include/fontEngine.i" tmp = $2 pTmp = $4 end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a string asm puts txa pha bit setROM lda evalStkL,x sta pTmp lda evalStkH,x sta pTmp+1 ldy #0 lda (pTmp),y tax iny - lda (pTmp),y ora #$80 jsr cout iny dex bne - bit setLcRW+lcBank2 pla tax rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a 16-bit hex value, followed by a space asm printHex bit setROM lda evalStkH,x jsr prbyte lda evalStkL,x jsr prbyte lda #$A0 jsr cout bit setLcRW+lcBank2 rts end asm crout bit setROM jsr crout dex ; don't-care return value bit setLcRW+lcBank2 rts end asm beep bit setROM jsr bell dex ; don't-care return value bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Work with the memory manager // Params: cmd, mainOrAux, amount asm loader txa pha bit setROM lda evalStkL+2,x ; command code pha lda evalStkL+1,x ; main or aux lsr ldy evalStkH,x ; address (or other param) lda evalStkL,x tax pla bcs + jsr mainLoader clc bcc ++ + jsr auxLoader ++ stx tmp pla tax inx ; drop second and third parameters inx lda tmp sta evalStkL,x tya sta evalStkH,x bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the font engine // Params: pFont asm initFontEngine txa pha bit setROM ldy evalStkL,x ; font engine likes *lo* byte in Y lda evalStkH,x ; hi byte in X tax jsr setFONT ; Set to write text on both hi-res pages at the same time lda #pHGR3 jsr displayMODE ; Set to normal (non-inverse) text lda #pNORMAL jsr drawMODE pla tax bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the ray casting engine // Params: pMap asm initRaycaster txa pha bit setROM lda evalStkL,x ldy evalStkH,x jsr $6000 pla tax bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Render one frame using the ray caster. // Params: none asm renderFrame txa pha bit setROM jsr $6003 pla tax dex ; don't-care return value bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Is the player's current position a physical obstruction? // Params: none asm isBlocked txa pha bit setROM jsr $6006 tay pla tax dex tya sta evalStkL,x lda #0 sta evalStkH,x bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Is there a script attached to the player's current position? // Params: none asm isScripted txa pha bit setROM jsr $6009 tay pla tax dex tya sta evalStkL,x lda #0 sta evalStkH,x bit setLcRW+lcBank2 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set either the sky or ground color // Params: (index<<8) | (colorCode) asm setColor txa pha bit setROM lda evalStkL,x ldy evalStkH,x jsr $600C bit setLcRW+lcBank2 pla tax rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Jump straight to the system monitor // Params: None asm goMon bit setROM jmp $FF69 end /////////////////////////////////////////////////////////////////////////////////////////////////// // Use the font engine to clear the current text window // Params: None asm clearWindow bit setROM txa pha jsr clearWINDOW bit setLcRW+lcBank2 pla tax dex ; don't-care return value rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a string using the font engine. Automatically splits lines to keep words from breaking. // Params: pStr asm displayStr bit setROM txa pha lda evalStkL,x sta pTmp lda evalStkH,x sta pTmp+1 ldy #0 lda (pTmp),y beq ++ inc pTmp bne + inc pTmp+1 + -- tax ldy #0 - lda (pTmp),y cmp #$20 bcc + cmp #$80 bcc ++ + lda #$C4 jsr $fded brk ++ dex cmp #$20 beq + sta $281,y iny cpx #0 bne - + sty $280 tya sec adc pTmp sta pTmp bcc + inc pTmp+1 + txa pha tya clc adc cursh sec sbc #2 cmp wndwdth bcc + lda #$D jsr printCHAR + ldy #$80 ldx #2 jsr printSTR lda cursh cmp wndleft beq + lda #$20 jsr printCHAR + pla bne -- ++ lda #$d jsr printCHAR bit setLcRW+lcBank2 pla tax rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // General methods def fatal(msg) loader(FATAL_ERROR, MAIN_MEM, msg) end def getUpperKey() byte key while ^keyboard < 128 loop key = ^keyboard & $7F ^keystrobe if key >= $60 key = key - $20 fin return key end def setSky(num) skyNum = num setColor(0<<8 | skyGndTbl1[skyNum]) setColor(1<<8 | skyGndTbl2[skyNum]) end def nextSky() setSky((skyNum + 1) & $F) end def setGround(num) groundNum = num setColor(2<<8 | skyGndTbl1[groundNum]) setColor(3<<8 | skyGndTbl2[groundNum]) end def nextGround() setGround((groundNum + 1) & $F) end def debugMem(bank, code) ^$c051 ^$c054 printHex(code) loader(DEBUG_MEM, bank, 0) getUpperKey() ^$c050 end def initMap() word scriptModule // Reset memory (our module will stay since memory manager locked it upon load) loader(RESET_MEMORY, MAIN_MEM, 0) // Load the font engine loader(SET_MEM_TARGET, MAIN_MEM, fontEngine) loader(QUEUE_LOAD, MAIN_MEM, RES_NUM_FONT_ENGINE<<8 | RES_TYPE_CODE) // Load the font for the font engine loader(SET_MEM_TARGET, MAIN_MEM, $9000) pFont = loader(QUEUE_LOAD, MAIN_MEM, 1<<8 | RES_TYPE_FONT) // Load the raycaster loader(SET_MEM_TARGET, MAIN_MEM, raycaster) loader(QUEUE_LOAD, MAIN_MEM, RES_NUM_RAYCASTER<<8 | RES_TYPE_CODE) // Load the texture expansion code loader(SET_MEM_TARGET, AUX_MEM, expandVec) loader(QUEUE_LOAD, AUX_MEM, RES_NUM_EXPAND_VEC<<8 | RES_TYPE_CODE) // Load the map pMap = loader(QUEUE_LOAD, MAIN_MEM, mapNum<<8 | RES_TYPE_3D_MAP) // Load everything that we just queued loader(FINISH_LOAD, MAIN_MEM, 1) // 1 = keep open // Load the frame image (and lock it there) if titleLoaded loader(UNLOCK_MEMORY,MAIN_MEM, $2000) loader(FREE_MEMORY, MAIN_MEM, $2000) titleLoaded = FALSE fin loader(SET_MEM_TARGET, MAIN_MEM, $2000) loader(QUEUE_LOAD, MAIN_MEM, 1<<8 | RES_TYPE_SCREEN) loader(LOCK_MEMORY, MAIN_MEM, $2000) // Load the scripts for this map, if it has any. scriptModule = pMap->2 // first 2 bytes are width and height, third byte is script module num if scriptModule pScripts = loader(QUEUE_LOAD, MAIN_MEM, (scriptModule << 8) | RES_TYPE_MODULE) fin loader(FINISH_LOAD, MAIN_MEM, 0) // 1 = keep open, for loading textures // Start up the font engine initFontEngine(pFont) // Start up the raycaster initRaycaster(pMap) // Set initial player position in case the init script doesn't do it ^playerDir = 1 *playerX = $280 *playerY = $380 // Initialize the map scripts setWindow2() clearWindow() prevX = -1 prevY = -1 prevScript = -1 nLocTrig = 0 prevMapNum = mapNum if pScripts *pScripts() fin // If we're returning to a map, resume from where the player left off if resetLocFromCache *playerX = cacheX *playerY = cacheY ^playerDir = cacheDir prevX = playerX.1 - 1 prevY = playerY.1 - 1 setSky(cacheSky) setGround(cacheGround) resetLocFromCache = FALSE fin // Draw the first frame renderFrame() redraw = FALSE end // Window for the map name bar def setWindow1() ^wndtop = 1 ^wndbtm = 2 ^wndleft = 4 ^wndwdth = 18 ^cursv = ^wndtop ^cursh = ^wndleft end // Window for the large upper right bar def setWindow2() ^wndtop = 3 ^wndbtm = 17 ^wndleft = 22 ^wndwdth = 37 ^cursv = ^wndtop ^cursh = ^wndleft end // Window for the mid-size lower right bar def setWindow3() ^wndtop = 18 ^wndbtm = 23 ^wndleft = 23 ^wndwdth = 37 ^cursv = ^wndtop ^cursh = ^wndleft end /////////////////////////////////////////////////////////////////////////////////////////////////// // Actions def initCmd(key, func) if key >= $60 key = key - $20 fin cmdTbl[key-$20] = func end def flipToFirstPage if ^frontBuf == 1 renderFrame() redraw = FALSE fin end def checkScript() word x word y word i word func x = playerX.1 - 1 y = playerY.1 - 1 if x <> prevX or y <> prevY prevX = x prevY = y if prevScript clearWindow() fin prevScript = NULL if isScripted() for i = 0 to nLocTrig-1 if x == locTrig_x[i] and y == locTrig_y[i] prevScript = locTrig_func[i] *prevScript() fin next fin fin if mapNum <> prevMapNum flipToFirstPage() initMap() fin end def moveForward() word wasBlocked word func wasBlocked = isBlocked() *playerX = *playerX + walkDirs[^playerDir << 1] *playerY = *playerY + walkDirs[(^playerDir << 1) + 1] if !wasBlocked and isBlocked() moveBackward() fin end def adjustDir(n) ^playerDir = (^playerDir + n) & $F end def moveBackward() adjustDir(8) moveForward() adjustDir(8) end def rotateLeft() adjustDir(-1) end def rotateRight() adjustDir(1) end def strafeRight() adjustDir(4) moveForward() adjustDir(-4) end def strafeLeft() adjustDir(-4) moveForward() adjustDir(4) end def setMap(is3d, num) // save player state if we're coming *from* the over-map if mapNum == OVERMAP cacheX = *playerX cacheY = *playerY cacheDir = ^playerDir cacheSky = skyNum cacheGround = groundNum else resetLocFromCache = TRUE fin mapNum = num end def nextMap() mapNum = mapNum + 1 if mapNum > 20 mapNum = 1 fin setMap(1, mapNum) end def teleport(x, y, dir) *playerX = ((x+1)<<8) | $80 *playerY = ((y+1)<<8) | $80 prevX = x prevY = y prevScript = -1 clearWindow() ^playerDir = dir redraw = TRUE end def kbdLoop() word key, func while TRUE key = getUpperKey() key = key - $20 if key >= 0 and key < $40 func = cmdTbl[key] if func func() renderFrame() redraw = FALSE checkScript() if redraw renderFrame() redraw = FALSE fin fin fin loop end def setLocationTrigger(x, y, func) if nLocTrig == MAX_LOC_TRIG fatal(@tooManyTriggers) fin locTrig_x[nLocTrig] = x locTrig_y[nLocTrig] = y locTrig_func[nLocTrig] = func nLocTrig = nLocTrig + 1 end def getYN() byte key while TRUE key = getUpperKey() if key == 'Y' return 1 elsif key == 'N' clearWindow() return 0 fin beep() loop end /////////////////////////////////////////////////////////////////////////////////////////////////// // Main code. // puts(@helloStr) // Init the command table initCmd('W', @moveForward) initCmd('A', @rotateLeft) initCmd('D', @rotateRight) initCmd('S', @moveBackward) initCmd('X', @moveBackward) initCmd('Z', @strafeLeft) initCmd('C', @strafeRight) initCmd('I', @moveForward) initCmd('J', @rotateLeft) initCmd('L', @rotateRight) initCmd('K', @moveBackward) initCmd(',', @moveBackward) initCmd('M', @strafeLeft) initCmd('.', @strafeRight) initCmd('N', @nextMap) initCmd('Y', @nextSky) initCmd('G', @nextGround) // $300 callbacks.0 = $4c callbacks:1 = @setLocationTrigger // $303 callbacks.3 = $4c callbacks:4 = @displayStr // $306 callbacks.6 = $4c callbacks:7 = @getYN // $309 callbacks.9 = $4c callbacks:10 = @setMap // $30C callbacks.12 = $4c callbacks:13 = @setSky // $30F callbacks.15 = $4c callbacks:16 = @setGround // $312 callbacks.18 = $4c callbacks:19 = @teleport // Load the title screen loader(SET_MEM_TARGET, MAIN_MEM, $2000) loader(QUEUE_LOAD, MAIN_MEM, 2<<8 | RES_TYPE_SCREEN) loader(LOCK_MEMORY, MAIN_MEM, $2000) loader(FINISH_LOAD, MAIN_MEM, 1) // 1 = keep open titleLoaded = TRUE ^$c050 ^$c057 ^$c054 ^$c052 // Hack for real (not emulated) IIc: sometimes displays only lo-bit graphics // unless we do this. *HUGE* thanks to Brendan Robert for the fix! ^$C07E=0 // disable double-hi-res ^$C05F // disable double-hi-res initMap() setWindow2() // Main keyboard loop puts(@loopStr) kbdLoop() done