;;; -*- TI-Asm -*- ;;; ;;; Mimas - Assembly language IDE for the TI-83 Plus ;;; ;;; Copyright (C) 2010 Benjamin Moody ;;; ;;; This program is free software: you can redistribute it and/or ;;; modify it under the terms of the GNU General Public License as ;;; published by the Free Software Foundation; either version 3 of the ;;; License, or (at your option) any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program. If not, see . ;; EditSectionAsm: ;; ;; Edit current section as assembly code. This routine does not ;; return. ;; ;; Input: ;; - curProgHeader contains program header ;; - curSectionHeader contains section header ;; - (editStartOffset) = offset to start of window ;; - (editLineOffset) = offset to selected line EditSectionAsm: ;; reset editTextChanged and editNewLine flags xor a ld (flags + asm_Flag3),a EditSectionAsm_Redisplay: call DisplayAsmEditor call RevertAsmLine EditSectionAsm_ContinueEditing: ld hl,flags + asm_Flag3 res editVarName,(hl) set editAsmShortcuts,(hl) set lwrCaseActive,(iy + appLwrCaseFlag) set appWantAlphaUpDn,(iy + alphaUpDnFlag) call EditTextLine EditSectionAsm_Keypress: SWITCH_BEGIN SWITCH_CASE kGraph, EditSectionAsm_QuitToSectionMenu SWITCH_CASE kBackspace, EditSectionAsm_DeleteLineBack SWITCH_CASE kUp, EditSectionAsm_Up SWITCH_CASE kClear, EditSectionAsm_DeleteLine SWITCH_CASE kDel, EditSectionAsm_DeleteLine SWITCH_CASE kDown, EditSectionAsm_Down SWITCH_CASE kAlphaUp, EditSectionAsm_PageUp SWITCH_CASE kAlphaDown, EditSectionAsm_PageDown SWITCH_CASE kEnter, EditSectionAsm_AppendLine SWITCH_CASE kIns, EditSectionAsm_InsertLine SWITCH_CASE kLastEnt, EditSectionAsm_RevertLine SWITCH_CASE kZoom, EditSectionAsm_CharacterMenu SWITCH_CASE kWindow, EditSectionAsm_ChooseBuiltinMenu SWITCH_CASE kAppsMenu, EditSectionAsm_BCALL SWITCH_CASE kVars, EditSectionAsm_SysAddrMenu SWITCH_CASE kCatalog, EditSectionAsm_SysAddrMenu SWITCH_CASE kYequ, EditSectionAsm_UserSymbolMenu SWITCH_CASE kMath, EditSectionAsm_OperatorMenu SWITCH_CASE kTest, EditSectionAsm_TestOperatorMenu SWITCH_CASE kVarx, EditSectionAsm_SpecialCharMenu SWITCH_CASE kPrgm, EditSectionAsm_InstructionMenu SWITCH_END cp kQuit jr nz,EditSectionAsm_ContinueEditing EditSectionAsm_QuitToSectionMenu: call CommitAsmLine jq nc,EditProgramSections EditSectionAsm_ContinueEditing_: jr EditSectionAsm_ContinueEditing EditSectionAsm_DeleteLineBack: ;; backspace (in empty buffer): clear current line and move to ;; previous call TextBufClear EditSectionAsm_Up: call CommitAsmLine jr c,EditSectionAsm_ContinueEditing call MoveAsmEditCursorUp EditSectionAsm_: jr EditSectionAsm EditSectionAsm_DeleteLine: ;; clear/delete (in empty buffer): clear current line and move to next call TextBufClear EditSectionAsm_Down: call CommitAsmLine jr c,EditSectionAsm_ContinueEditing call nz,MoveAsmEditCursorDown jr EditSectionAsm_ EditSectionAsm_PageUp: ;; alpha+up: move up 7 lines call CommitAsmLine jr c,EditSectionAsm_ContinueEditing ld b,7 EditSectionAsm_PageUpLoop: call MoveAsmEditCursorUp djnz EditSectionAsm_PageUpLoop jr EditSectionAsm_ EditSectionAsm_PageDown: ;; alpha+down: move down 7 lines call CommitAsmLine jr c,EditSectionAsm_ContinueEditing ld b,7 EditSectionAsm_PageDownLoop: call MoveAsmEditCursorDown djnz EditSectionAsm_PageDownLoop jr EditSectionAsm_ EditSectionAsm_AppendLine: ;; enter: add new line after the current one call CommitAsmLine jr c,EditSectionAsm_ContinueEditing_ call nz,MoveAsmEditCursorDown EditSectionAsm_DoInsert: set editNewLine,(iy + asm_Flag3) EditSectionAsm_RevertLine: EditSectionAsm_Redisplay_: jq_ EditSectionAsm_Redisplay EditSectionAsm_InsertLine: ;; 2nd+insert: add new line before the current one call CommitAsmLine jr c,EditSectionAsm_ContinueEditing_ jr EditSectionAsm_DoInsert EditSectionAsm_CharacterMenu: ld hl,1 ld (alphaMenuTabStart),hl ld l,0EDh ld (alphaMenuTabEnd),hl ld bc,Extract_Char ld de,SeekCompare_Char_pZStr ld hl,nullStr call ShowAlphaMenu jr c,EditSectionAsm_ContinueEditing_ ld a,(hl) call TextEditInsertChar EditSectionAsm_ContinueEditing__: jr EditSectionAsm_ContinueEditing_ EditSectionAsm_ChooseBuiltinMenu: SWITCH_NUMBERED_MENU asmEditBuiltinMenuInfo SWITCH_CASE 1, EditSectionAsm_ROMCallMenu SWITCH_CASE 2, EditSectionAsm_SysAddrMenu SWITCH_CASE 3, EditSectionAsm_ConstantMenu SWITCH_CASE 4, EditSectionAsm_FlagMenu SWITCH_END jr EditSectionAsm_ContinueEditing__ EditSectionAsm_BCALL: ;; apps: pop up B_CALL menu; insert " BCALL " if buffer is ;; currently empty call TextBufMostlyEmpty jr nz,EditSectionAsm_ROMCallMenu call TextBufClear ld hl,smnem_BCALL + specialMnemonics call TextBufInsertMnemonic call TextEditMoveCursorCurrent ; call DispTextHead ; call XBufCpy EditSectionAsm_ROMCallMenu: ld hl,romCallNames ld de,romCallNamesEnd jr EditSectionAsm_BuiltinSymbolMenu EditSectionAsm_SysAddrMenu: ld hl,sysAddrNames ld de,sysAddrNamesEnd jr EditSectionAsm_BuiltinSymbolMenu EditSectionAsm_ConstantMenu: ld hl,sysEnumNames ld de,sysEnumNamesEnd jr EditSectionAsm_BuiltinSymbolMenu EditSectionAsm_FlagMenu: ld hl,sysFlagNames ld de,sysFlagNamesEnd EditSectionAsm_BuiltinSymbolMenu: ld (alphaMenuTabStart),hl ld (alphaMenuTabEnd),de ld bc,Extract_pI16SStr ld de,SeekCompare_pI16SStr_pZStr jr EditSectionAsm_PopupAlphaMenu EditSectionAsm_UserSymbolMenu: ld hl,(curProgHeader + PROGHEADER_SYMBOL_STRING_START) inc hl ; skip sentinel ld (alphaMenuTabStart),hl ld hl,(curProgHeader + PROGHEADER_SYMBOL_STRING_END) ld (alphaMenuTabEnd),hl ld bc,Extract_oI8SStr ld de,SeekCompare_oI8SStr_pZStr jr EditSectionAsm_PopupAlphaMenu EditSectionAsm_OperatorMenu: SWITCH_NUMBERED_MENU asmEditOperatorMenuInfo SWITCH_CASE 1, EditSectionAsm_ArithmeticOperatorMenu SWITCH_CASE 2, EditSectionAsm_BitwiseOperatorMenu SWITCH_CASE 3, EditSectionAsm_TestOperatorMenu SWITCH_END jr EditSectionAsm_ContinueEditing__ EditSectionAsm_TestOperatorMenu: ld hl,asmEditTestMenuInfo jr EditSectionAsm_PopupOperatorMenu EditSectionAsm_SpecialCharMenu: ld hl,asmEditSpecialCharMenuInfo jr EditSectionAsm_PopupOperatorMenu EditSectionAsm_InstructionMenu: call TextBufAtBOB ld a,SFourSpaces call z,TextEditInsertChar ld hl,normalMnemonics ld (alphaMenuTabStart),hl ld hl,normalMnemonicsEnd ld (alphaMenuTabEnd),hl ld bc,Extract_pMStr ld de,SeekCompare_pMStr_pZStr EditSectionAsm_PopupAlphaMenu: ;; Add a zero terminator at the cursor location, so we can ;; pass a buffer pointer directly to ShowAlphaMenu. If there ;; isn't room for a zero terminator, there won't be room to ;; type a symbol! xor a call TextBufInsertCharNF jr c,EditSectionAsm_ContinueEditing___ call TextBufDeletePrevCharNF ;; Find the beginning of the partial symbol, if any, that has ;; been typed ld hl,(editCursor) call FindWordStart inc hl push hl call ShowAlphaMenu pop de jr c,EditSectionAsm_ContinueEditing___ ;; User chose a symbol. Delete the previous partial symbol ;; and insert the new string push hl EditSectionAsm_SymbolMenu_DeleteLoop: ld hl,(editCursor) scf sbc hl,de jr c,EditSectionAsm_SymbolMenu_DeleteDone call TextEditLeft call TextEditDeleteNext jr EditSectionAsm_SymbolMenu_DeleteLoop EditSectionAsm_SymbolMenu_InsertLoop: ld a,(hl) inc hl push hl call TextEditInsertChar EditSectionAsm_SymbolMenu_DeleteDone: pop hl EditSectionAsm_InsertOperator: ld a,(hl) and ~20h jr nz,EditSectionAsm_SymbolMenu_InsertLoop EditSectionAsm_ContinueEditing___: jq_ EditSectionAsm_ContinueEditing EditSectionAsm_BitwiseOperatorMenu: ld hl,asmEditBitwiseMenuInfo jr EditSectionAsm_PopupOperatorMenu EditSectionAsm_ArithmeticOperatorMenu: ld hl,asmEditArithmeticMenuInfo EditSectionAsm_PopupOperatorMenu: call ShowNumberedMenu or a jr z,EditSectionAsm_ContinueEditing___ ex de,hl jr EditSectionAsm_InsertOperator ;; DisplayAsmEditor: ;; ;; Display the contents of the assembly editor. ;; ;; Input: ;; - (editStartOffset) = offset to start of window ;; - (editLineOffset) = offset to selected row ;; - editNewLine, (iy + asm_Flag3) = 1 if editing a new line ;; ;; Output: ;; - (penRow) = row to be edited ;; - (textEditXStart) = 0 ;; - (textEditXEnd) = 95 ;; ;; Destroys: ;; - AF, BC, DE, HL, IX ;; - textBuffer ;; - instrBuf ;; - OP1-OP6 DisplayAsmEditor: call ClearBufferSetGraph ld hl,asmEditSoftKeys call DisplaySoftKeys ld hl,progTextBufferStart ld (hl),0 ; sentinel, so we can use ; FindWordStart to find start of a ; partial symbol that the user has ; typed inc hl ld (editTop),hl ld hl,progTextBufferEnd ld (editBtm),hl ld hl,(editStartOffset) ld a,3 ld (minPenCol),a push af ld a,-6 ld (penRow),a DisplayAsmEditor_Loop: ld de,(editLineOffset) or a sbc hl,de add hl,de jr z,DisplayAsmEditor_SelectedRow DisplayAsmEditor_LoopNotSelected: ld de,(curSectionHeader + SECTHEADER_DATA_END) or a sbc hl,de add hl,de jr nc,DisplayAsmEditor_Finished ;; non-selected row: format and display instruction call VNewLine cp SCREEN_HEIGHT - 13 jr nc,DisplayAsmEditor_Finished push hl call GetProgramInstr push hl call FormatInstruction call TextBufToZTStr call BufVPutS pop hl ld a,(hl) and 3Fh add a,2 pop hl jr DisplayAsmEditor_NextInstruction DisplayAsmEditor_SelectedRow: ;; selected row: skip to next instruction call VNewLine pop bc push af ; save selected penRow for later ;; check if at end of section ex de,hl ld hl,(curSectionHeader + SECTHEADER_DATA_END) or a sbc hl,de ex de,hl ; DE = number of bytes remaining jr c,DisplayAsmEditor_SelectedRowEOS jr z,DisplayAsmEditor_SelectedRowEOS bit editNewLine,(iy + asm_Flag3) jr nz,DisplayAsmEditor_SelectedRowEmpty ;; get size of instruction call GetProgramDataByte and 3Fh add a,2 ;; if A >= DE, clamp size of instruction ;; (obviously this won't help fix corrupted data, but it ;; should make it possible for the user to delete corrupted ;; data without causing any further damage) inc d dec d jr nz,DisplayAsmEditor_SelectedRowSizeOK cp e jr c,DisplayAsmEditor_SelectedRowSizeOK ld a,e DisplayAsmEditor_SelectedRowSizeOK: ld (editLineSize),a DisplayAsmEditor_NextInstruction: ;; move to next instruction (A = length of instruction) ld b,0 ld c,a add hl,bc jr DisplayAsmEditor_Loop DisplayAsmEditor_SelectedRowEOS: ld hl,(curSectionHeader + SECTHEADER_DATA_END) ld (editLineOffset),hl set editNewLine,(iy + asm_Flag3) DisplayAsmEditor_SelectedRowEmpty: xor a ld (editLineSize),a jr DisplayAsmEditor_LoopNotSelected DisplayAsmEditor_Finished: pop af ld (penRow),a ld hl,5F00h ld (textEditXStart),hl ret ;; MoveAsmEditCursorUp: ;; ;; Move to the previous line of this section. ;; ;; Input: ;; - (editStartOffset) = offset to start of window ;; - (editLineOffset) = offset to selected line ;; ;; Output: ;; - (editStartOffset) and (editLineOffset) updated ;; ;; Destroys: ;; - AF, HL, DE ;; - OP1 MoveAsmEditCursorUp: ld hl,(editStartOffset) ld de,(editLineOffset) or a sbc hl,de add hl,de jr nc,MoveAsmEditCursorUp_TopOfWindow call PrevProgramInstr ld (editLineOffset),hl ret MoveAsmEditCursorUp_TopOfWindow: ld hl,(curSectionHeader + SECTHEADER_DATA_START) call PrevProgramInstr ld (editStartOffset),hl ld (editLineOffset),hl ret ;; MoveAsmEditCursorDown: ;; ;; Move to the next line of this section. ;; ;; Input: ;; - (editStartOffset) = offset to start of window ;; - (editLineOffset) = offset to selected line ;; ;; Output: ;; - (editStartOffset) and (editLineOffset) updated ;; ;; Destroys: ;; - AF, HL, DE ;; - OP1 MoveAsmEditCursorDown: ld hl,(editLineOffset) call NextProgramInstr ex de,hl ;; don't let user move past end of section ld hl,(curSectionHeader + SECTHEADER_DATA_END) or a sbc hl,de ret c ld (editLineOffset),de ;; find end of window ld hl,(editStartOffset) bit editNewLine,(iy + asm_Flag3) call z,NextProgramInstr push bc ld b,7 MoveAsmEditCursorDown_CheckLoop: call NextProgramInstr djnz MoveAsmEditCursorDown_CheckLoop pop bc ;; check if cursor is in window or a ex de,hl sbc hl,de ret c ld hl,(editStartOffset) call NextProgramInstr ld (editStartOffset),hl ret ;; NextProgramInstr: ;; ;; Skip to the next program instruction. ;; ;; Input: ;; - HL = program offset to start of an instruction ;; ;; Output: ;; - HL = program offset to start of next instruction ;; ;; Destroys: ;; - AF ;; - OP1 NextProgramInstr: call GetProgramDataByte and 3Fh add a,2 add a,l ld l,a ld a,0 adc a,h ld h,a ret ;; PrevProgramInstr: ;; ;; Skip to the previous program instruction. ;; ;; Input: ;; - DE = program offset to start of an instruction ;; - HL = anchor instruction offset (e.g., start of section or start ;; of window) ;; ;; Output: ;; - HL = program offset to start of previous instruction ;; ;; Destroys: ;; - AF ;; - OP1 PrevProgramInstr_Loop: pop af PrevProgramInstr: push hl call NextProgramInstr sbc hl,de add hl,de jr c,PrevProgramInstr_Loop pop hl ret ;; CommitAsmLine: ;; ;; Replace contents of current program line with the result of parsing ;; the text edit buffer. ;; ;; Input: ;; - Text buffer contains text to parse ;; - (editLineOffset) = location in program where the parsed ;; instruction should be placed ;; - (editLineSize) = number of bytes at that location to replace (0 ;; if we're editing a new line, 2 to 65 if we're editing an existing ;; line) ;; ;; Output: ;; - Carry set if an error occurred ;; - Zero set if buffer was empty ;; ;; Destroys: ;; - AF, BC, DE, HL, IX ;; - OP1-OP6 ;; - instrBuf ;; - tempExprBuf CommitAsmLine: bit editTextChanged,(iy + asm_Flag3) jr z,CommitAsmLine_NothingToDo call ResetInstrBuf ld hl,CommitAsmLine_Error call APP_PUSH_ERRORH call TextBufMostlyEmpty jr z,CommitAsmLine_Empty call TextEditEOL call TextBufToZTStr push hl pop ix call ParseInstruction jr nc,CommitAsmLine_Success push ix pop de call TextEditMoveCursor ld hl,emsg_SyntaxError call ThrowLineError ;; UNREACHABLE CommitAsmLine_Empty: xor a jr CommitAsmLine_SizeA CommitAsmLine_Success: ld a,(instrBuf) and 3Fh add a,2 CommitAsmLine_SizeA: ld c,a ; C = size of new instruction ld hl,(editLineOffset) ld a,(editLineSize) ld b,a ; B = size of old instruction or a push af push bc push hl ;; Make a copy of old instruction (don't unref yet, since ;; InsDelProgramCodeMem may throw an error) ld de,OP1 ld bc,65 call nz,CopyProgramData pop hl pop bc push bc ;; Insert/delete memory to make room for the new ;; instruction ld a,c sub b ; A = amount to insert ld c,a sbc a,a ld b,a call InsDelProgramCodeMem pop bc ;; Copy new instruction into program ;; C = size of new instruction ;; DE = newly allocated memory xor a ld b,a or c jr z,CommitAsmLine_Finish_Empty ld hl,instrBuf ldir CommitAsmLine_Finish_Empty: call ResetInstrBuf ; in case UnrefInstruction throws an error pop af ;; Unref symbols used by old instruction, if any ld hl,OP1 call nz,UnrefInstruction call APP_POP_ERRORH CommitAsmLine_NothingToDo: call TextBufMostlyEmpty scf ccf ret CommitAsmLine_Error: push af ;; this should never throw an error - if instrBuf contains ;; any symbol references, that means that ParseInstruction ;; succeeded and reffed them ld hl,instrBuf call UnrefInstruction pop af push af call ShowErrorPopup pop af cp E_AppErr1 | 80h scf ret z ;; if we got a system error (such as out-of-memory), then ;; revert user's changes - in particular, if the program we're ;; editing is archived and we don't have enough memory to ;; unarchive it, we don't want to get the user stuck in an ;; infinite loop RevertAsmLine: call TextBufClear ld a,SFourSpaces call TextBufInsertChar bit editNewLine,(iy + asm_Flag3) jr nz,RevertAsmLine_Empty ld hl,(editLineOffset) call GetProgramInstr call FormatInstruction ;; if the buffer is full, put an invalid character at the end ;; - if for some reason the formatted instruction doesn't fit ;; in the buffer, there's not much we can do, but it's better ;; for things to fail in a way that the user will notice ld a,Sblock ld (progTextBufferEnd - 1),a RevertAsmLine_Empty: res editTextChanged,(iy + asm_Flag3) call TextEditBOL scf ret